Car settings match the phone's: voice settings under Sound and voice, switches for on and off, screen fixes together under Screen, and one lyrics setup for everyone

This commit is contained in:
Seb3thehacker committed 2026-11-06 12:04:28 -08:00
1 parent ef5365b9ed
commit afd05ad8c0
23 files changed
+113 -267

No files matched your search

@@ -68,7 +68,6 @@ private const val KEY_LOCATION_WARNING = "car_location_warning"
private const val KEY_AUTOPLAY = "car_autoplay" private const val KEY_AUTOPLAY = "car_autoplay"
private const val KEY_PLAYER_TITLE = "car_player_title" private const val KEY_PLAYER_TITLE = "car_player_title"
private const val KEY_PLAYER_LEFT = "car_player_left" private const val KEY_PLAYER_LEFT = "car_player_left"
private const val KEY_MORE_LYRICS = "car_more_lyrics"
private const val KEY_THEME = "car_app_theme" private const val KEY_THEME = "car_app_theme"
private const val KEY_UI_THEME = "car_ui_theme" private const val KEY_UI_THEME = "car_ui_theme"
private const val KEY_DARK_STYLE = "car_dark_style" private const val KEY_DARK_STYLE = "car_dark_style"
@@ -114,7 +113,6 @@ private const val KEY_CLUSTER_VIDEO = "cluster_video_enabled"
private val autoplayFlow = MutableStateFlow(false) private val autoplayFlow = MutableStateFlow(false)
private val playerTitleFlow = MutableStateFlow(false) private val playerTitleFlow = MutableStateFlow(false)
private val playerLeftFlow = MutableStateFlow(false) private val playerLeftFlow = MutableStateFlow(false)
private val moreLyricsFlow = MutableStateFlow(true)
private val themeFlow = MutableStateFlow(AppTheme.PHONE) private val themeFlow = MutableStateFlow(AppTheme.PHONE)
val appTheme: StateFlow<AppTheme> = themeFlow val appTheme: StateFlow<AppTheme> = themeFlow
private val darkStyleFlow = MutableStateFlow(DarkStyle.BLACK) private val darkStyleFlow = MutableStateFlow(DarkStyle.BLACK)
@@ -245,7 +243,6 @@ private const val KEY_CLUSTER_VIDEO = "cluster_video_enabled"
val playerLeft: StateFlow<Boolean> = playerLeftFlow val playerLeft: StateFlow<Boolean> = playerLeftFlow
/** Lyrics from BetterLyrics and KuGou as well as LRCLIB; off sends song details to LRCLIB only. */ /** Lyrics from BetterLyrics and KuGou as well as LRCLIB; off sends song details to LRCLIB only. */
val moreLyrics: StateFlow<Boolean> = moreLyricsFlow
val scale: StateFlow<Float> = scaleFlow val scale: StateFlow<Float> = scaleFlow
@@ -398,7 +395,6 @@ private const val KEY_CLUSTER_VIDEO = "cluster_video_enabled"
autoplayFlow.value = AppSettings.getString(app, KEY_AUTOPLAY, "false") == "true" autoplayFlow.value = AppSettings.getString(app, KEY_AUTOPLAY, "false") == "true"
playerTitleFlow.value = AppSettings.getString(app, KEY_PLAYER_TITLE, "false") == "true" playerTitleFlow.value = AppSettings.getString(app, KEY_PLAYER_TITLE, "false") == "true"
playerLeftFlow.value = AppSettings.getString(app, KEY_PLAYER_LEFT, "false") == "true" playerLeftFlow.value = AppSettings.getString(app, KEY_PLAYER_LEFT, "false") == "true"
moreLyricsFlow.value = AppSettings.getString(app, KEY_MORE_LYRICS, "true") == "true"
pipeAudioFlow.value = AppSettings.getString(app, KEY_PIPE_AUDIO, "true") == "true" pipeAudioFlow.value = AppSettings.getString(app, KEY_PIPE_AUDIO, "true") == "true"
// Until the driver sets it, on for USB audio and off for Bluetooth, where the map app's // Until the driver sets it, on for USB audio and off for Bluetooth, where the map app's
// own voice already reaches the car. // own voice already reaches the car.
@@ -490,11 +486,6 @@ private const val KEY_CLUSTER_VIDEO = "cluster_video_enabled"
AppSettings.putString(app, KEY_PLAYER_LEFT, on.toString()) AppSettings.putString(app, KEY_PLAYER_LEFT, on.toString())
} }
fun setMoreLyrics(on: Boolean) {
moreLyricsFlow.value = on
AppSettings.putString(app, KEY_MORE_LYRICS, on.toString())
}
fun setAutoplay(on: Boolean) { fun setAutoplay(on: Boolean) {
autoplayFlow.value = on autoplayFlow.value = on
AppSettings.putString(app, KEY_AUTOPLAY, on.toString()) AppSettings.putString(app, KEY_AUTOPLAY, on.toString())
@@ -32,7 +32,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@@ -45,9 +44,6 @@ import app.seb3thehacker.gearslip.car.theme.GsColors
import app.seb3thehacker.gearslip.car.theme.GsIconBox import app.seb3thehacker.gearslip.car.theme.GsIconBox
import app.seb3thehacker.gearslip.car.theme.GsThemes import app.seb3thehacker.gearslip.car.theme.GsThemes
import app.seb3thehacker.gearslip.AppSettings import app.seb3thehacker.gearslip.AppSettings
import app.seb3thehacker.gearslip.ui.CertSummary
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
/** /**
* Settings that make sense while sitting in the car. The web page URL is edited here, with the * Settings that make sense while sitting in the car. The web page URL is edited here, with the
@@ -73,7 +69,6 @@ fun CarSettingsScreen() {
val autoplay by CarSettings.autoplay.collectAsState() val autoplay by CarSettings.autoplay.collectAsState()
val playerTitle by CarSettings.playerTitle.collectAsState() val playerTitle by CarSettings.playerTitle.collectAsState()
val playerLeft by CarSettings.playerLeft.collectAsState() val playerLeft by CarSettings.playerLeft.collectAsState()
val moreLyrics by CarSettings.moreLyrics.collectAsState()
val theme by CarSettings.appTheme.collectAsState() val theme by CarSettings.appTheme.collectAsState()
val uiTheme by CarSettings.uiTheme.collectAsState() val uiTheme by CarSettings.uiTheme.collectAsState()
val darkStyle by CarSettings.darkStyle.collectAsState() val darkStyle by CarSettings.darkStyle.collectAsState()
@@ -85,12 +80,9 @@ fun CarSettingsScreen() {
val display by CarEnvironment.display.collectAsState() val display by CarEnvironment.display.collectAsState()
val vehicle by CarEnvironment.vehicle.collectAsState() val vehicle by CarEnvironment.vehicle.collectAsState()
val context = LocalContext.current val context = LocalContext.current
val cert by produceState<CertSummary?>(null) {
value = withContext(Dispatchers.Default) { CertSummary.read(context) }
}
// A rail of the groups down the left, and the chosen group's settings beside it: five short // A rail of the groups down the left, and the chosen group's settings beside it: five short
// lists instead of one long scroll. // lists instead of one long scroll. The groups and their order match Settings on the phone.
var chosen by androidx.compose.runtime.saveable.rememberSaveable { androidx.compose.runtime.mutableIntStateOf(0) } var chosen by androidx.compose.runtime.saveable.rememberSaveable { androidx.compose.runtime.mutableIntStateOf(0) }
val listState = androidx.compose.foundation.lazy.rememberLazyListState() val listState = androidx.compose.foundation.lazy.rememberLazyListState()
androidx.compose.runtime.LaunchedEffect(chosen) { listState.scrollToItem(0) } androidx.compose.runtime.LaunchedEffect(chosen) { listState.scrollToItem(0) }
@@ -104,35 +96,31 @@ fun CarSettingsScreen() {
contentPadding = PaddingValues(horizontal = 24.dp, vertical = 16.dp), contentPadding = PaddingValues(horizontal = 24.dp, vertical = 16.dp),
verticalArrangement = Arrangement.spacedBy(20.dp), verticalArrangement = Arrangement.spacedBy(20.dp),
) { ) {
var section: Int when (chosen) {
section = 0 0 -> {
if (section == chosen) item { Group(stringResource(R.string.car_group_screen)) } item { Group(stringResource(R.string.car_group_screen)) }
item {
if (section == chosen) item {
ChoiceRow( ChoiceRow(
stringResource(R.string.car_open_on_connect), stringResource(R.string.car_open_on_connect),
listOf(CarSettings.HOME to stringResource(R.string.choice_open_home)) + CarApps.shown(launcher, LauncherMode.canCall(context)).map { it.id to stringResource(it.labelRes) }, listOf(CarSettings.HOME to stringResource(R.string.choice_open_home)) + CarApps.shown(launcher, LauncherMode.canCall(context)).map { it.id to stringResource(it.labelRes) },
open, open,
) { CarSettings.setOpenOnConnect(it) } ) { CarSettings.setOpenOnConnect(it) }
} }
item {
if (section == chosen) item {
ChoiceRow( ChoiceRow(
stringResource(R.string.car_size), stringResource(R.string.car_size),
listOf(1f to stringResource(R.string.choice_size_normal), 1.25f to stringResource(R.string.choice_size_large), 1.5f to stringResource(R.string.choice_size_xlarge)), listOf(1f to stringResource(R.string.choice_size_normal), 1.25f to stringResource(R.string.choice_size_large), 1.5f to stringResource(R.string.choice_size_xlarge)),
scale, scale,
) { CarSettings.setScale(it) } ) { CarSettings.setScale(it) }
} }
item {
if (section == chosen) item {
ChoiceRow( ChoiceRow(
stringResource(R.string.car_app_theme), stringResource(R.string.car_app_theme),
listOf(AppTheme.PHONE to stringResource(R.string.choice_theme_phone), AppTheme.LIGHT to stringResource(R.string.choice_theme_light), AppTheme.DARK to stringResource(R.string.choice_theme_dark)), listOf(AppTheme.PHONE to stringResource(R.string.choice_theme_phone), AppTheme.LIGHT to stringResource(R.string.choice_theme_light), AppTheme.DARK to stringResource(R.string.choice_theme_dark)),
theme, theme,
) { CarSettings.setAppTheme(it) } ) { CarSettings.setAppTheme(it) }
} }
if (theme != AppTheme.LIGHT) item {
if (section == chosen && theme != AppTheme.LIGHT) item {
ChoiceRow( ChoiceRow(
stringResource(R.string.car_dark_mode), stringResource(R.string.car_dark_mode),
listOf( listOf(
@@ -144,33 +132,31 @@ fun CarSettingsScreen() {
darkStyle, darkStyle,
) { CarSettings.setDarkStyle(it) } ) { CarSettings.setDarkStyle(it) }
} }
item {
if (section == chosen) item {
ChoiceRow( ChoiceRow(
stringResource(R.string.carset_theme), stringResource(R.string.car_buttons),
GsThemes.builtIn.map { it.id to it.name }, GsThemes.builtIn.map { it.id to it.name },
uiTheme, uiTheme,
) { CarSettings.setUiTheme(it) } ) { CarSettings.setUiTheme(it) }
} }
item {
if (section == chosen) item {
ChoiceRow( ChoiceRow(
stringResource(R.string.car_map_day_night), stringResource(R.string.car_map_day_night),
listOf(NightMode.AUTO to stringResource(R.string.choice_auto), NightMode.DAY to stringResource(R.string.choice_night_day), NightMode.NIGHT to stringResource(R.string.choice_night_night)), listOf(NightMode.AUTO to stringResource(R.string.choice_auto), NightMode.DAY to stringResource(R.string.choice_night_day), NightMode.NIGHT to stringResource(R.string.choice_night_night)),
night, night,
) { CarSettings.setNightMode(it) } ) { CarSettings.setNightMode(it) }
} }
item {
if (section == chosen) item {
ChoiceRow( ChoiceRow(
stringResource(R.string.car_units), stringResource(R.string.car_units),
listOf(Units.AUTO to stringResource(R.string.choice_auto), Units.IMPERIAL to stringResource(R.string.carset_imperial), Units.METRIC to stringResource(R.string.carset_metric)), listOf(Units.AUTO to stringResource(R.string.choice_auto), Units.IMPERIAL to stringResource(R.string.carset_imperial), Units.METRIC to stringResource(R.string.carset_metric)),
units, units,
) { CarSettings.setUnits(it) } ) { CarSettings.setUnits(it) }
} }
// Fixes for a head unit that gets its own screen wrong (issue #12), together
// For a head unit that reports the wrong size and stretches the picture (issue #12). // and last, since most cars never need them.
if (section == chosen) item { item { SubGroup(stringResource(R.string.carset_advanced)) }
item {
ChoiceRow( ChoiceRow(
stringResource(R.string.carset_screen_size), stringResource(R.string.carset_screen_size),
listOf<ScreenSize?>(null).plus(SCREEN_PRESETS).plus(screenSize).distinct() listOf<ScreenSize?>(null).plus(SCREEN_PRESETS).plus(screenSize).distinct()
@@ -178,19 +164,18 @@ fun CarSettingsScreen() {
screenSize, screenSize,
) { CarSettings.setScreenSize(it) } ) { CarSettings.setScreenSize(it) }
} }
if (screenSize != null) item {
if (section == chosen && screenSize != null) item {
ChoiceRow( ChoiceRow(
stringResource(R.string.carset_fit), stringResource(R.string.carset_fit),
listOf(ScreenFit.CROP to stringResource(R.string.screen_crop), ScreenFit.SQUASH to stringResource(R.string.screen_squash)), listOf(ScreenFit.CROP to stringResource(R.string.screen_crop), ScreenFit.SQUASH to stringResource(R.string.screen_squash)),
screenFit, screenFit,
) { CarSettings.setScreenFit(it) } ) { CarSettings.setScreenFit(it) }
} }
item { ScreenFit(vehicle) }
section = 1 }
if (section == chosen) item { Group(stringResource(R.string.car_group_nav_bar)) } 1 -> {
item { Group(stringResource(R.string.car_group_nav_bar)) }
if (section == chosen) item { item {
ChoiceRow( ChoiceRow(
stringResource(R.string.car_nav_bar), stringResource(R.string.car_nav_bar),
listOf( listOf(
@@ -201,156 +186,82 @@ fun CarSettingsScreen() {
navBar, navBar,
) { CarSettings.setNavBarMode(it) } ) { CarSettings.setNavBarMode(it) }
} }
item {
if (section == chosen) item {
ChoiceRow( ChoiceRow(
stringResource(R.string.car_player_nav_bar), stringResource(R.string.car_player_nav_bar),
listOf(false to stringResource(R.string.car_skip_buttons), true to stringResource(R.string.car_song_title)), listOf(false to stringResource(R.string.car_skip_buttons), true to stringResource(R.string.car_song_title)),
playerTitle, playerTitle,
) { CarSettings.setPlayerTitle(it) } ) { CarSettings.setPlayerTitle(it) }
} }
item {
if (section == chosen) item {
ChoiceRow( ChoiceRow(
stringResource(R.string.car_player_beside_map), stringResource(R.string.car_player_beside_map),
listOf(false to stringResource(R.string.car_right), true to stringResource(R.string.car_left)), listOf(false to stringResource(R.string.car_right), true to stringResource(R.string.car_left)),
playerLeft, playerLeft,
) { CarSettings.setPlayerLeft(it) } ) { CarSettings.setPlayerLeft(it) }
} }
item { Note(stringResource(R.string.car_player_beside_map_note)) }
if (section == chosen) item { item {
ChoiceRow( SwitchRow(stringResource(R.string.car_autoplay), stringResource(R.string.car_autoplay_note), autoplay) { CarSettings.setAutoplay(it) }
stringResource(R.string.car_autoplay),
listOf(false to stringResource(R.string.off), true to stringResource(R.string.on)),
autoplay,
) { CarSettings.setAutoplay(it) }
} }
if (section == chosen) item {
ChoiceRow(
stringResource(R.string.carset_lyrics_from),
listOf(false to "LRCLIB", true to stringResource(R.string.carset_more_sources)),
moreLyrics,
) { CarSettings.setMoreLyrics(it) }
} }
2 -> {
section = 2 item { Group(stringResource(R.string.car_group_sound)) }
if (section == chosen) item { Group(stringResource(R.string.car_group_sound)) } item {
if (section == chosen) item {
ChoiceRow( ChoiceRow(
stringResource(R.string.car_audio), stringResource(R.string.car_audio),
listOf(false to stringResource(R.string.guide_audio_bluetooth), true to stringResource(R.string.guide_audio_usb)), listOf(false to stringResource(R.string.guide_audio_bluetooth), true to stringResource(R.string.guide_audio_usb)),
pipeAudio, pipeAudio,
) { CarSettings.setPipeAudio(it) } ) { CarSettings.setPipeAudio(it) }
} }
item { Note(stringResource(R.string.carset_audio_note)) }
if (section == chosen) item { item {
Text( SwitchRow(stringResource(R.string.car_spoken_directions), stringResource(R.string.carset_directions_note), speakDirections) { CarSettings.setSpeakDirections(it) }
stringResource(R.string.carset_audio_note),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
} }
if (speakDirections) item {
section = 3 SwitchRow(stringResource(R.string.car_directions_over_music), stringResource(R.string.car_directions_over_music_note), directionsOverMusic) { CarSettings.setDirectionsOverMusic(it) }
if (section == chosen) item { Group(stringResource(R.string.car_group_warnings)) }
if (section == chosen && !pipeAudio) item {
ChoiceRow(
stringResource(R.string.car_warn_bluetooth),
listOf(false to stringResource(R.string.off), true to stringResource(R.string.on)),
bluetoothWarning,
) { CarSettings.setBluetoothWarning(it) }
} }
item {
if (section == chosen) item { SwitchRow(stringResource(R.string.car_phone_speech), stringResource(R.string.carset_phone_speech_note), systemSpeech) { CarSettings.setSystemSpeech(it) }
ChoiceRow(
stringResource(R.string.car_warn_location),
listOf(false to stringResource(R.string.off), true to stringResource(R.string.on)),
locationWarning,
) { CarSettings.setLocationWarning(it) }
} }
if (section == chosen) item {
ChoiceRow(
stringResource(R.string.car_spoken_directions),
listOf(false to stringResource(R.string.off), true to stringResource(R.string.on)),
speakDirections,
) { CarSettings.setSpeakDirections(it) }
} }
3 -> {
if (section == chosen) item { item { Group(stringResource(R.string.car_group_warnings)) }
Text( if (!pipeAudio) item {
stringResource(R.string.carset_directions_note), SwitchRow(stringResource(R.string.car_warn_bluetooth), null, bluetoothWarning) { CarSettings.setBluetoothWarning(it) }
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
} }
item {
if (section == chosen && speakDirections) item { SwitchRow(stringResource(R.string.car_warn_location), null, locationWarning) { CarSettings.setLocationWarning(it) }
ChoiceRow(
stringResource(R.string.car_directions_over_music),
listOf(false to stringResource(R.string.off), true to stringResource(R.string.on)),
directionsOverMusic,
) { CarSettings.setDirectionsOverMusic(it) }
} }
if (section == chosen && speakDirections) item {
Text(
stringResource(R.string.car_directions_over_music_note),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
} }
else -> {
if (section == chosen) item { item { Group(stringResource(R.string.car_group_more)) }
ExperimentalFeatureRow( item { WebPageSetting() }
title = stringResource(R.string.car_phone_speech), item {
description = stringResource(R.string.carset_phone_speech_note), SwitchRow(stringResource(R.string.car_experimental), stringResource(R.string.car_experimental_note), experimentalFeatures) { CarSettings.setExperimentalFeaturesEnabled(it) }
checked = systemSpeech,
) { CarSettings.setSystemSpeech(it) }
} }
if (experimentalFeatures) item {
section = 4 Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
if (section == chosen) item { Group(stringResource(R.string.car_group_more)) } SubGroup(stringResource(R.string.carset_experimental_heading))
if (section == chosen) item {
WebPageSetting()
}
if (section == chosen) item {
ScreenFit(vehicle)
}
if (section == chosen) item {
ChoiceRow(
stringResource(R.string.car_experimental),
listOf(false to stringResource(R.string.carset_hide), true to stringResource(R.string.carset_show)),
experimentalFeatures,
) { CarSettings.setExperimentalFeaturesEnabled(it) }
}
if (section == chosen && experimentalFeatures) item {
ExperimentalFeaturesList(carSensors, adaptiveVideo, clusterVideo) ExperimentalFeaturesList(carSensors, adaptiveVideo, clusterVideo)
} }
}
if (section == chosen) item { item {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(stringResource(R.string.settings_connection), style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary) SubGroup(stringResource(R.string.settings_connection))
// The certificate is managed on the phone, which shows it in full.
val none = stringResource(R.string.status_idle)
Info(stringResource(R.string.carset_phone), "${Build.MANUFACTURER} ${Build.MODEL}") Info(stringResource(R.string.carset_phone), "${Build.MANUFACTURER} ${Build.MODEL}")
Info(stringResource(R.string.carset_vehicle), vehicle.ifEmpty { "-" }) Info(stringResource(R.string.carset_vehicle), vehicle.ifEmpty { none })
Info(stringResource(R.string.carset_video), display.ifEmpty { "-" }) Info(stringResource(R.string.carset_video), display.ifEmpty { none })
Info(stringResource(R.string.settings_certificate), cert?.headline ?: stringResource(R.string.cert_checking))
} }
} }
item {
if (section == chosen) item {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(stringResource(R.string.settings_about), style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary) SubGroup(stringResource(R.string.settings_about))
Info(stringResource(R.string.carset_version), app.seb3thehacker.gearslip.BuildConfig.VERSION_NAME) Info(stringResource(R.string.carset_version), app.seb3thehacker.gearslip.BuildConfig.VERSION_NAME)
Info(stringResource(R.string.carset_build), app.seb3thehacker.gearslip.BuildConfig.VERSION_CODE.toString()) Info(stringResource(R.string.carset_build), app.seb3thehacker.gearslip.BuildConfig.VERSION_CODE.toString())
ExperimentalFeatureRow( SwitchRow(
stringResource(R.string.carset_whats_new_after), stringResource(R.string.carset_whats_new_after),
stringResource(R.string.carset_whats_new_after_sub), stringResource(R.string.carset_whats_new_after_sub),
whatsNew, whatsNew,
@@ -363,6 +274,20 @@ fun CarSettingsScreen() {
} }
} }
} }
}
}
/** A heading inside a group, for a few settings that belong together. */
@Composable
private fun SubGroup(title: String) {
Text(title, style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary)
}
/** What a setting above it does, in a sentence or two. */
@Composable
private fun Note(text: String) {
Text(text, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
}
@Composable @Composable
private fun <T> ChoiceRow(title: String, options: List<Pair<T, String>>, selected: T, onSelect: (T) -> Unit) { private fun <T> ChoiceRow(title: String, options: List<Pair<T, String>>, selected: T, onSelect: (T) -> Unit) {
@@ -451,7 +376,16 @@ private fun <T> ChoiceButtons(options: List<Pair<T, String>>, selected: T, onSel
else GsColors(scheme.surfaceContainerHigh, scheme.onSurface), else GsColors(scheme.surfaceContainerHigh, scheme.onSurface),
latched = on, latched = on,
) { ) {
Text(label, style = MaterialTheme.typography.titleMedium, fontWeight = if (on) FontWeight.Bold else FontWeight.Normal) // Centred however many lines it takes: "Black and colour" wrapped to the left.
Text(
label,
style = MaterialTheme.typography.titleMedium,
fontWeight = if (on) FontWeight.Bold else FontWeight.Normal,
textAlign = androidx.compose.ui.text.style.TextAlign.Center,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(horizontal = 8.dp),
)
} }
} }
} }
@@ -469,17 +403,17 @@ private fun ExperimentalFeaturesList(carSensors: Boolean, adaptiveVideo: Boolean
Modifier.fillMaxWidth(), Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(10.dp), verticalArrangement = Arrangement.spacedBy(10.dp),
) { ) {
ExperimentalFeatureRow( SwitchRow(
title = stringResource(R.string.car_vehicle_data), title = stringResource(R.string.car_vehicle_data),
description = stringResource(R.string.carset_vehicle_data_note), description = stringResource(R.string.carset_vehicle_data_note),
checked = carSensors, checked = carSensors,
) { CarSettings.setCarSensorsEnabled(it) } ) { CarSettings.setCarSensorsEnabled(it) }
ExperimentalFeatureRow( SwitchRow(
title = stringResource(R.string.car_adaptive_video), title = stringResource(R.string.car_adaptive_video),
description = stringResource(R.string.carset_adaptive_note), description = stringResource(R.string.carset_adaptive_note),
checked = adaptiveVideo, checked = adaptiveVideo,
) { CarSettings.setAdaptiveVideoEnabled(it) } ) { CarSettings.setAdaptiveVideoEnabled(it) }
ExperimentalFeatureRow( SwitchRow(
title = stringResource(R.string.car_cluster_video), title = stringResource(R.string.car_cluster_video),
description = stringResource(R.string.car_cluster_video_note), description = stringResource(R.string.car_cluster_video_note),
checked = clusterVideo, checked = clusterVideo,
@@ -488,9 +422,9 @@ private fun ExperimentalFeaturesList(carSensors: Boolean, adaptiveVideo: Boolean
} }
@Composable @Composable
private fun ExperimentalFeatureRow( private fun SwitchRow(
title: String, title: String,
description: String, description: String?,
checked: Boolean, checked: Boolean,
warning: Boolean = false, warning: Boolean = false,
onCheckedChange: (Boolean) -> Unit, onCheckedChange: (Boolean) -> Unit,
@@ -508,7 +442,7 @@ private fun ExperimentalFeatureRow(
) { ) {
Column(Modifier.weight(1f)) { Column(Modifier.weight(1f)) {
Text(title, style = MaterialTheme.typography.titleMedium) Text(title, style = MaterialTheme.typography.titleMedium)
Text( if (description != null) Text(
description, description,
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = if (warning) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurfaceVariant, color = if (warning) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurfaceVariant,
@@ -589,7 +523,7 @@ private fun ScreenFit(vehicle: String) {
} }
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(stringResource(R.string.carset_screen_fit), style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary) Text(stringResource(R.string.carset_screen_fit), style = MaterialTheme.typography.titleMedium)
Stepper(stringResource(R.string.carset_top), insets.top) { change(insets.copy(top = (insets.top + it).coerceAtLeast(0))) } Stepper(stringResource(R.string.carset_top), insets.top) { change(insets.copy(top = (insets.top + it).coerceAtLeast(0))) }
Stepper(stringResource(R.string.carset_bottom), insets.bottom) { change(insets.copy(bottom = (insets.bottom + it).coerceAtLeast(0))) } Stepper(stringResource(R.string.carset_bottom), insets.bottom) { change(insets.copy(bottom = (insets.bottom + it).coerceAtLeast(0))) }
Text( Text(
@@ -597,14 +531,15 @@ private fun ScreenFit(vehicle: String) {
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
) )
GsButton( // With no car to name, a greyed-out button only said what couldn't be done.
if (known) GsButton(
onClick = { onClick = {
val ok = app.seb3thehacker.gearslip.VehicleProfiles.saveInsets(context, vehicle, insets) val ok = app.seb3thehacker.gearslip.VehicleProfiles.saveInsets(context, vehicle, insets)
saved = if (ok) context.getString(R.string.carset_saved_to, vehicle) else context.getString(R.string.carset_save_failed) saved = if (ok) context.getString(R.string.carset_saved_to, vehicle) else context.getString(R.string.carset_save_failed)
}, },
enabled = known,
modifier = Modifier.fillMaxWidth().height(56.dp), modifier = Modifier.fillMaxWidth().height(56.dp),
) { Text(if (known) stringResource(R.string.carset_save_vehicle) else stringResource(R.string.carset_no_profile)) } ) { Text(stringResource(R.string.carset_save_vehicle)) }
else Note(stringResource(R.string.carset_fit_needs_car))
saved?.let { Text(it, style = MaterialTheme.typography.bodyMedium) } saved?.let { Text(it, style = MaterialTheme.typography.bodyMedium) }
} }
} }
@@ -4,7 +4,6 @@ import android.content.Context
import android.util.Base64 import android.util.Base64
import android.util.Xml import android.util.Xml
import app.seb3thehacker.gearslip.GearslipLog import app.seb3thehacker.gearslip.GearslipLog
import app.seb3thehacker.gearslip.car.CarSettings
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
@@ -48,8 +47,7 @@ class LyricsResult(val lines: List<LyricLine>, val synced: Boolean, val source:
* *
* Media apps almost never publish lyrics in their session metadata, so these are the only general * Media apps almost never publish lyrics in their session metadata, so these are the only general
* source. Each lookup sends the track's title, artist, album and length to these services, which * source. Each lookup sends the track's title, artist, album and length to these services, which
* is why it happens only when the driver opens the lyrics view, never in the background. With * is why it happens only when the driver opens the lyrics view, never in the background.
* [CarSettings.moreLyrics] off, only LRCLIB is asked.
* *
* Results are cached twice over: in memory for the life of the process, and on disk under the * Results are cached twice over: in memory for the life of the process, and on disk under the
* cache directory so a song looked up on a past drive doesn't need the network again on this one. * cache directory so a song looked up on a past drive doesn't need the network again on this one.
@@ -79,13 +77,12 @@ object Lyrics {
private class Source(val name: String, val best: Int, val lookup: () -> LyricsResult?) private class Source(val name: String, val best: Int, val lookup: () -> LyricsResult?)
private fun sources(title: String, artist: String, album: String, durationMs: Long): List<Source> { private fun sources(title: String, artist: String, album: String, durationMs: Long): List<Source> {
val more = CarSettings.moreLyrics.value
val seconds = (durationMs / 1000).toInt() val seconds = (durationMs / 1000).toInt()
return buildList { return listOf(
if (more) add(Source("BetterLyrics", BEST) { betterLyrics(title, artist, album, seconds) }) Source("BetterLyrics", BEST) { betterLyrics(title, artist, album, seconds) },
add(Source("LRCLIB", 2) { exact(title, artist, album, durationMs) ?: search(title, artist) }) Source("LRCLIB", 2) { exact(title, artist, album, durationMs) ?: search(title, artist) },
if (more) add(Source("KuGou", 2) { kuGou(title, artist, album, seconds) }) Source("KuGou", 2) { kuGou(title, artist, album, seconds) },
} )
} }
private fun key(title: String, artist: String, album: String, durationMs: Long) = "$title|$artist|$album|$durationMs" private fun key(title: String, artist: String, album: String, durationMs: Long) = "$title|$artist|$album|$durationMs"
@@ -93,8 +90,7 @@ object Lyrics {
suspend fun find(context: Context, title: String, artist: String, album: String, durationMs: Long): LyricsResult? { suspend fun find(context: Context, title: String, artist: String, album: String, durationMs: Long): LyricsResult? {
if (title.isBlank()) return null if (title.isBlank()) return null
val key = key(title, artist, album, durationMs) val key = key(title, artist, album, durationMs)
// A miss with only LRCLIB isn't a miss once more sources are on, so the mode is part of it. val memoryKey = key
val memoryKey = "${CarSettings.moreLyrics.value}|$key"
synchronized(cache) { if (cache.containsKey(memoryKey)) return cache[memoryKey] } synchronized(cache) { if (cache.containsKey(memoryKey)) return cache[memoryKey] }
val fromDisk = withContext(Dispatchers.IO) { readDisk(context, key) } val fromDisk = withContext(Dispatchers.IO) { readDisk(context, key) }
@@ -129,7 +125,7 @@ object Lyrics {
withContext(Dispatchers.IO) { writeDisk(context, key, better ?: current, System.currentTimeMillis()) } withContext(Dispatchers.IO) { writeDisk(context, key, better ?: current, System.currentTimeMillis()) }
if (better == null) return null if (better == null) return null
GearslipLog.i("lyrics: replaced ${current.source.ifEmpty { "cached" }} lyrics with ${better.source}'s") GearslipLog.i("lyrics: replaced ${current.source.ifEmpty { "cached" }} lyrics with ${better.source}'s")
synchronized(cache) { cache["${CarSettings.moreLyrics.value}|$key"] = better } synchronized(cache) { cache[key] = better }
return better return better
} }
@@ -228,7 +228,6 @@ private fun GroupRows(group: CarGroup) {
val autoplay by CarSettings.autoplay.collectAsState() val autoplay by CarSettings.autoplay.collectAsState()
val playerTitle by CarSettings.playerTitle.collectAsState() val playerTitle by CarSettings.playerTitle.collectAsState()
val playerLeft by CarSettings.playerLeft.collectAsState() val playerLeft by CarSettings.playerLeft.collectAsState()
val moreLyrics by CarSettings.moreLyrics.collectAsState()
val theme by CarSettings.appTheme.collectAsState() val theme by CarSettings.appTheme.collectAsState()
val uiTheme by CarSettings.uiTheme.collectAsState() val uiTheme by CarSettings.uiTheme.collectAsState()
val darkStyle by CarSettings.darkStyle.collectAsState() val darkStyle by CarSettings.darkStyle.collectAsState()
@@ -263,16 +262,9 @@ private fun GroupRows(group: CarGroup) {
SettingsDivider() SettingsDivider()
Choice(stringResource(R.string.car_player_nav_bar), listOf(false to stringResource(R.string.car_skip_buttons), true to stringResource(R.string.car_song_title)), playerTitle, CarSettings::setPlayerTitle) Choice(stringResource(R.string.car_player_nav_bar), listOf(false to stringResource(R.string.car_skip_buttons), true to stringResource(R.string.car_song_title)), playerTitle, CarSettings::setPlayerTitle)
SettingsDivider() SettingsDivider()
Choice(stringResource(R.string.car_player_beside_map), listOf(false to stringResource(R.string.car_right), true to stringResource(R.string.car_left)), playerLeft, CarSettings::setPlayerLeft) Choice(stringResource(R.string.car_player_beside_map), listOf(false to stringResource(R.string.car_right), true to stringResource(R.string.car_left)), playerLeft, CarSettings::setPlayerLeft, note = stringResource(R.string.car_player_beside_map_note))
SettingsDivider() SettingsDivider()
Toggle(stringResource(R.string.car_autoplay), autoplay, CarSettings::setAutoplay) Toggle(stringResource(R.string.car_autoplay), autoplay, CarSettings::setAutoplay, note = stringResource(R.string.car_autoplay_note))
SettingsDivider()
Toggle(
stringResource(R.string.car_more_lyrics),
moreLyrics,
CarSettings::setMoreLyrics,
note = stringResource(R.string.car_more_lyrics_note),
)
} }
CarGroup.SOUND -> { CarGroup.SOUND -> {
Toggle( Toggle(
@@ -309,7 +301,7 @@ private fun GroupRows(group: CarGroup) {
CarGroup.MORE -> { CarGroup.MORE -> {
WebPageRow() WebPageRow()
SettingsDivider() SettingsDivider()
Toggle(stringResource(R.string.car_experimental), experimental, CarSettings::setExperimentalFeaturesEnabled) Toggle(stringResource(R.string.car_experimental), experimental, CarSettings::setExperimentalFeaturesEnabled, note = stringResource(R.string.car_experimental_note))
if (experimental) { if (experimental) {
SettingsDivider() SettingsDivider()
Toggle( Toggle(
@@ -200,8 +200,6 @@
<string name="car_right">يمين</string> <string name="car_right">يمين</string>
<string name="car_left">يسار</string> <string name="car_left">يسار</string>
<string name="car_autoplay">التشغيل التلقائي عند الاتصال</string> <string name="car_autoplay">التشغيل التلقائي عند الاتصال</string>
<string name="car_more_lyrics">مصادر أكثر لكلمات الأغاني</string>
<string name="car_more_lyrics_note">يسأل أيضًا BetterLyrics وKuGou، فيحصلان على اسم الأغنية والفنان. عند الإيقاف يسأل LRCLIB فقط.</string>
<string name="car_spoken_directions">الإرشادات الصوتية</string> <string name="car_spoken_directions">الإرشادات الصوتية</string>
<string name="car_spoken_directions_note">أوقفها إذا كان صوت تطبيق الخرائط نفسه يُسمع في السيارة بالفعل.</string> <string name="car_spoken_directions_note">أوقفها إذا كان صوت تطبيق الخرائط نفسه يُسمع في السيارة بالفعل.</string>
<string name="car_phone_speech">استخدام التعرّف على الكلام في الهاتف</string> <string name="car_phone_speech">استخدام التعرّف على الكلام في الهاتف</string>
@@ -433,8 +431,6 @@
<string name="carset_fit">ملاءمة الشاشة</string> <string name="carset_fit">ملاءمة الشاشة</string>
<string name="off">متوقف</string> <string name="off">متوقف</string>
<string name="on">مفعّل</string> <string name="on">مفعّل</string>
<string name="carset_lyrics_from">الكلمات من</string>
<string name="carset_more_sources">مصادر أكثر</string>
<string name="carset_hide">إخفاء</string> <string name="carset_hide">إخفاء</string>
<string name="carset_show">إظهار</string> <string name="carset_show">إظهار</string>
<string name="carset_phone">الهاتف</string> <string name="carset_phone">الهاتف</string>
@@ -200,8 +200,6 @@
<string name="car_right">Vpravo</string> <string name="car_right">Vpravo</string>
<string name="car_left">Vlevo</string> <string name="car_left">Vlevo</string>
<string name="car_autoplay">Přehrát po připojení</string> <string name="car_autoplay">Přehrát po připojení</string>
<string name="car_more_lyrics">Více zdrojů textů</string>
<string name="car_more_lyrics_note">Ptá se také BetterLyrics a KuGou, které pak dostanou název a interpreta skladby. Vypnuto se ptá jen LRCLIB.</string>
<string name="car_spoken_directions">Hlasové pokyny</string> <string name="car_spoken_directions">Hlasové pokyny</string>
<string name="car_spoken_directions_note">Vypněte je, pokud hlas vaší mapové aplikace už v autě hraje.</string> <string name="car_spoken_directions_note">Vypněte je, pokud hlas vaší mapové aplikace už v autě hraje.</string>
<string name="car_phone_speech">Používat rozpoznávání řeči telefonu</string> <string name="car_phone_speech">Používat rozpoznávání řeči telefonu</string>
@@ -431,8 +429,6 @@
<string name="carset_fit">Přizpůsobit obrazovce</string> <string name="carset_fit">Přizpůsobit obrazovce</string>
<string name="off">Vyp.</string> <string name="off">Vyp.</string>
<string name="on">Zap.</string> <string name="on">Zap.</string>
<string name="carset_lyrics_from">Texty z</string>
<string name="carset_more_sources">Více zdrojů</string>
<string name="carset_hide">Skrýt</string> <string name="carset_hide">Skrýt</string>
<string name="carset_show">Zobrazit</string> <string name="carset_show">Zobrazit</string>
<string name="carset_phone">Telefon</string> <string name="carset_phone">Telefon</string>
@@ -187,8 +187,6 @@
<string name="car_right">Højre</string> <string name="car_right">Højre</string>
<string name="car_left">Venstre</string> <string name="car_left">Venstre</string>
<string name="car_autoplay">Autoplay på forbindelse</string> <string name="car_autoplay">Autoplay på forbindelse</string>
<string name="car_more_lyrics">Flere tekstkilder</string>
<string name="car_more_lyrics_note">Spørg også BetterLyrics og KuGou, som derefter får sangens navn og kunstner. Ellers spørges kun LRCLIB.</string>
<string name="cert_import_button">Import</string> <string name="cert_import_button">Import</string>
<string name="car_spoken_directions">Talte rutevejledniger</string> <string name="car_spoken_directions">Talte rutevejledniger</string>
<string name="car_spoken_directions_note">Slå det fra, hvis dit navigations programs egen stemme allerede spiller i bilen.</string> <string name="car_spoken_directions_note">Slå det fra, hvis dit navigations programs egen stemme allerede spiller i bilen.</string>
@@ -11,8 +11,6 @@
<string name="carset_fit">An Bildschirm anpassen</string> <string name="carset_fit">An Bildschirm anpassen</string>
<string name="off">Aus</string> <string name="off">Aus</string>
<string name="on">An</string> <string name="on">An</string>
<string name="carset_lyrics_from">Songtexte von</string>
<string name="carset_more_sources">Weitere Quellen</string>
<string name="carset_hide">Ausblenden</string> <string name="carset_hide">Ausblenden</string>
<string name="carset_show">Anzeigen</string> <string name="carset_show">Anzeigen</string>
<string name="carset_phone">Handy</string> <string name="carset_phone">Handy</string>
@@ -510,8 +508,6 @@
<string name="car_right">Rechts</string> <string name="car_right">Rechts</string>
<string name="car_left">Links</string> <string name="car_left">Links</string>
<string name="car_autoplay">Automatisch abspielen beim Verbinden</string> <string name="car_autoplay">Automatisch abspielen beim Verbinden</string>
<string name="car_more_lyrics">Weitere Songtext-Quellen</string>
<string name="car_more_lyrics_note">Fragt außerdem bei BetterLyrics und KuGou an, die dabei Titel und Interpret des Songs erfahren. Bei „Aus“ wird nur LRCLIB gefragt.</string>
<string name="car_spoken_directions">Gesprochene Navigationsansagen</string> <string name="car_spoken_directions">Gesprochene Navigationsansagen</string>
<string name="car_spoken_directions_note">Schalte das aus, wenn die Sprachansage deiner Karten-App bereits im Auto zu hören ist.</string> <string name="car_spoken_directions_note">Schalte das aus, wenn die Sprachansage deiner Karten-App bereits im Auto zu hören ist.</string>
<string name="car_phone_speech">Spracherkennung des Handys verwenden</string> <string name="car_phone_speech">Spracherkennung des Handys verwenden</string>
@@ -109,8 +109,6 @@
<string name="car_right">Bien</string> <string name="car_right">Bien</string>
<string name="car_left">Izquierda</string> <string name="car_left">Izquierda</string>
<string name="car_autoplay">Autoplay en conexión</string> <string name="car_autoplay">Autoplay en conexión</string>
<string name="car_more_lyrics">Más fuentes de letras</string>
<string name="car_more_lyrics_note">También pregunta BetterLyrics y KuGou, que luego consigue el nombre y artista de la canción. Fuera pide LRCLIB sólo.</string>
<string name="car_spoken_directions">Instrucciones descritas</string> <string name="car_spoken_directions">Instrucciones descritas</string>
<string name="car_spoken_directions_note">Apágalo si la propia voz de su aplicación de mapa ya juega en el coche.</string> <string name="car_spoken_directions_note">Apágalo si la propia voz de su aplicación de mapa ya juega en el coche.</string>
<string name="car_phone_speech">Use el reconocimiento del discurso del teléfono</string> <string name="car_phone_speech">Use el reconocimiento del discurso del teléfono</string>
@@ -300,8 +298,6 @@
<string name="carset_fit">Ajuste a la pantalla</string> <string name="carset_fit">Ajuste a la pantalla</string>
<string name="off">Fuera</string> <string name="off">Fuera</string>
<string name="on">On</string> <string name="on">On</string>
<string name="carset_lyrics_from">Letras de</string>
<string name="carset_more_sources">Más fuentes</string>
<string name="carset_hide">Oculto</string> <string name="carset_hide">Oculto</string>
<string name="carset_show">Show</string> <string name="carset_show">Show</string>
<string name="carset_phone">Teléfono</string> <string name="carset_phone">Teléfono</string>
@@ -200,8 +200,6 @@
<string name="car_right">Droite</string> <string name="car_right">Droite</string>
<string name="car_left">Gauche</string> <string name="car_left">Gauche</string>
<string name="car_autoplay">Lecture auto à la connexion</string> <string name="car_autoplay">Lecture auto à la connexion</string>
<string name="car_more_lyrics">Plus de sources de paroles</string>
<string name="car_more_lyrics_note">Interroge aussi BetterLyrics et KuGou, qui reçoivent alors le titre et l’artiste du morceau. Désactivé, seul LRCLIB est interrogé.</string>
<string name="car_spoken_directions">Guidage vocal</string> <string name="car_spoken_directions">Guidage vocal</string>
<string name="car_spoken_directions_note">Désactivez-le si la voix de votre appli de navigation passe déjà dans la voiture.</string> <string name="car_spoken_directions_note">Désactivez-le si la voix de votre appli de navigation passe déjà dans la voiture.</string>
<string name="car_phone_speech">Utiliser la reconnaissance vocale du téléphone</string> <string name="car_phone_speech">Utiliser la reconnaissance vocale du téléphone</string>
@@ -430,8 +428,6 @@
<string name="carset_fit">Ajuster à l’écran</string> <string name="carset_fit">Ajuster à l’écran</string>
<string name="off">Désactivé</string> <string name="off">Désactivé</string>
<string name="on">Activé</string> <string name="on">Activé</string>
<string name="carset_lyrics_from">Paroles depuis</string>
<string name="carset_more_sources">Plus de sources</string>
<string name="carset_hide">Masquer</string> <string name="carset_hide">Masquer</string>
<string name="carset_show">Afficher</string> <string name="carset_show">Afficher</string>
<string name="carset_phone">Téléphone</string> <string name="carset_phone">Téléphone</string>
@@ -200,8 +200,6 @@
<string name="car_right">Destra</string> <string name="car_right">Destra</string>
<string name="car_left">Sinistra</string> <string name="car_left">Sinistra</string>
<string name="car_autoplay">Riproduci alla connessione</string> <string name="car_autoplay">Riproduci alla connessione</string>
<string name="car_more_lyrics">Più fonti per i testi</string>
<string name="car_more_lyrics_note">Chiede anche a BetterLyrics e KuGou, che ricevono così titolo e artista del brano. Se disattivato, chiede solo a LRCLIB.</string>
<string name="car_spoken_directions">Indicazioni vocali</string> <string name="car_spoken_directions">Indicazioni vocali</string>
<string name="car_spoken_directions_note">Disattivale se la voce della tua app di navigazione si sente già in auto.</string> <string name="car_spoken_directions_note">Disattivale se la voce della tua app di navigazione si sente già in auto.</string>
<string name="car_phone_speech">Usa il riconoscimento vocale del telefono</string> <string name="car_phone_speech">Usa il riconoscimento vocale del telefono</string>
@@ -430,8 +428,6 @@
<string name="carset_fit">Adatta allo schermo</string> <string name="carset_fit">Adatta allo schermo</string>
<string name="off">Off</string> <string name="off">Off</string>
<string name="on">On</string> <string name="on">On</string>
<string name="carset_lyrics_from">Testi da</string>
<string name="carset_more_sources">Più fonti</string>
<string name="carset_hide">Nascondi</string> <string name="carset_hide">Nascondi</string>
<string name="carset_show">Mostra</string> <string name="carset_show">Mostra</string>
<string name="carset_phone">Telefono</string> <string name="carset_phone">Telefono</string>
@@ -200,8 +200,6 @@
<string name="car_right">右</string> <string name="car_right">右</string>
<string name="car_left">左</string> <string name="car_left">左</string>
<string name="car_autoplay">接続時に自動再生</string> <string name="car_autoplay">接続時に自動再生</string>
<string name="car_more_lyrics">歌詞の取得元を増やす</string>
<string name="car_more_lyrics_note">BetterLyrics と KuGou にも問い合わせ、曲名とアーティスト名が送られます。オフにすると LRCLIB のみに問い合わせます。</string>
<string name="car_spoken_directions">音声案内</string> <string name="car_spoken_directions">音声案内</string>
<string name="car_spoken_directions_note">地図アプリ自身の音声がすでに車で流れている場合はオフにしてください。</string> <string name="car_spoken_directions_note">地図アプリ自身の音声がすでに車で流れている場合はオフにしてください。</string>
<string name="car_phone_speech">スマートフォンの音声認識を使う</string> <string name="car_phone_speech">スマートフォンの音声認識を使う</string>
@@ -428,8 +426,6 @@
<string name="carset_fit">画面に合わせる</string> <string name="carset_fit">画面に合わせる</string>
<string name="off">オフ</string> <string name="off">オフ</string>
<string name="on">オン</string> <string name="on">オン</string>
<string name="carset_lyrics_from">歌詞の取得元</string>
<string name="carset_more_sources">取得元を増やす</string>
<string name="carset_hide">隠す</string> <string name="carset_hide">隠す</string>
<string name="carset_show">表示</string> <string name="carset_show">表示</string>
<string name="carset_phone">電話</string> <string name="carset_phone">電話</string>
@@ -200,8 +200,6 @@
<string name="car_right">오른쪽</string> <string name="car_right">오른쪽</string>
<string name="car_left">왼쪽</string> <string name="car_left">왼쪽</string>
<string name="car_autoplay">연결 시 자동 재생</string> <string name="car_autoplay">연결 시 자동 재생</string>
<string name="car_more_lyrics">가사 출처 더 보기</string>
<string name="car_more_lyrics_note">BetterLyrics와 KuGou에도 요청하며, 이때 노래 제목과 아티스트가 전달됩니다. 끄면 LRCLIB에만 요청합니다.</string>
<string name="car_spoken_directions">음성 안내</string> <string name="car_spoken_directions">음성 안내</string>
<string name="car_spoken_directions_note">지도 앱 자체의 음성이 이미 차량에서 나온다면 끄세요.</string> <string name="car_spoken_directions_note">지도 앱 자체의 음성이 이미 차량에서 나온다면 끄세요.</string>
<string name="car_phone_speech">휴대전화의 음성 인식 사용</string> <string name="car_phone_speech">휴대전화의 음성 인식 사용</string>
@@ -428,8 +426,6 @@
<string name="carset_fit">화면에 맞추기</string> <string name="carset_fit">화면에 맞추기</string>
<string name="off">끄기</string> <string name="off">끄기</string>
<string name="on">켜기</string> <string name="on">켜기</string>
<string name="carset_lyrics_from">가사 출처</string>
<string name="carset_more_sources">출처 더 보기</string>
<string name="carset_hide">숨기기</string> <string name="carset_hide">숨기기</string>
<string name="carset_show">표시</string> <string name="carset_show">표시</string>
<string name="carset_phone">휴대전화</string> <string name="carset_phone">휴대전화</string>
@@ -200,8 +200,6 @@
<string name="car_right">Rechts</string> <string name="car_right">Rechts</string>
<string name="car_left">Links</string> <string name="car_left">Links</string>
<string name="car_autoplay">Automatisch afspelen bij verbinden</string> <string name="car_autoplay">Automatisch afspelen bij verbinden</string>
<string name="car_more_lyrics">Meer songtekstbronnen</string>
<string name="car_more_lyrics_note">Vraagt ook BetterLyrics en KuGou, die dan de titel en artiest van het nummer krijgen. Uit vraagt alleen LRCLIB.</string>
<string name="car_spoken_directions">Gesproken aanwijzingen</string> <string name="car_spoken_directions">Gesproken aanwijzingen</string>
<string name="car_spoken_directions_note">Zet dit uit als de stem van je kaarten-app al in de auto klinkt.</string> <string name="car_spoken_directions_note">Zet dit uit als de stem van je kaarten-app al in de auto klinkt.</string>
<string name="car_phone_speech">Spraakherkenning van de telefoon gebruiken</string> <string name="car_phone_speech">Spraakherkenning van de telefoon gebruiken</string>
@@ -429,8 +427,6 @@
<string name="carset_fit">Passend maken voor het scherm</string> <string name="carset_fit">Passend maken voor het scherm</string>
<string name="off">Uit</string> <string name="off">Uit</string>
<string name="on">Aan</string> <string name="on">Aan</string>
<string name="carset_lyrics_from">Songteksten van</string>
<string name="carset_more_sources">Meer bronnen</string>
<string name="carset_hide">Verbergen</string> <string name="carset_hide">Verbergen</string>
<string name="carset_show">Tonen</string> <string name="carset_show">Tonen</string>
<string name="carset_phone">Telefoon</string> <string name="carset_phone">Telefoon</string>
@@ -200,8 +200,6 @@
<string name="car_right">Prawo</string> <string name="car_right">Prawo</string>
<string name="car_left">Lewo</string> <string name="car_left">Lewo</string>
<string name="car_autoplay">Odtwarzaj po połączeniu</string> <string name="car_autoplay">Odtwarzaj po połączeniu</string>
<string name="car_more_lyrics">Więcej źródeł tekstów</string>
<string name="car_more_lyrics_note">Pyta też BetterLyrics i KuGou, które dostają wtedy tytuł i wykonawcę utworu. Po wyłączeniu pyta tylko LRCLIB.</string>
<string name="car_spoken_directions">Wskazówki głosowe</string> <string name="car_spoken_directions">Wskazówki głosowe</string>
<string name="car_spoken_directions_note">Wyłącz, jeśli głos twojej aplikacji z mapami już gra w samochodzie.</string> <string name="car_spoken_directions_note">Wyłącz, jeśli głos twojej aplikacji z mapami już gra w samochodzie.</string>
<string name="car_phone_speech">Używaj rozpoznawania mowy telefonu</string> <string name="car_phone_speech">Używaj rozpoznawania mowy telefonu</string>
@@ -431,8 +429,6 @@
<string name="carset_fit">Dopasuj do ekranu</string> <string name="carset_fit">Dopasuj do ekranu</string>
<string name="off">Wył.</string> <string name="off">Wył.</string>
<string name="on">Wł.</string> <string name="on">Wł.</string>
<string name="carset_lyrics_from">Teksty z</string>
<string name="carset_more_sources">Więcej źródeł</string>
<string name="carset_hide">Ukryj</string> <string name="carset_hide">Ukryj</string>
<string name="carset_show">Pokaż</string> <string name="carset_show">Pokaż</string>
<string name="carset_phone">Telefon</string> <string name="carset_phone">Telefon</string>
@@ -200,8 +200,6 @@
<string name="car_right">Direita</string> <string name="car_right">Direita</string>
<string name="car_left">Esquerda</string> <string name="car_left">Esquerda</string>
<string name="car_autoplay">Tocar ao conectar</string> <string name="car_autoplay">Tocar ao conectar</string>
<string name="car_more_lyrics">Mais fontes de letras</string>
<string name="car_more_lyrics_note">Também consulta o BetterLyrics e o KuGou, que recebem o nome e o artista da música. Desativado, consulta só o LRCLIB.</string>
<string name="car_spoken_directions">Instruções faladas</string> <string name="car_spoken_directions">Instruções faladas</string>
<string name="car_spoken_directions_note">Desative se a voz do seu app de mapas já toca no carro.</string> <string name="car_spoken_directions_note">Desative se a voz do seu app de mapas já toca no carro.</string>
<string name="car_phone_speech">Usar o reconhecimento de voz do celular</string> <string name="car_phone_speech">Usar o reconhecimento de voz do celular</string>
@@ -430,8 +428,6 @@
<string name="carset_fit">Ajustar à tela</string> <string name="carset_fit">Ajustar à tela</string>
<string name="off">Desativado</string> <string name="off">Desativado</string>
<string name="on">Ativado</string> <string name="on">Ativado</string>
<string name="carset_lyrics_from">Letras de</string>
<string name="carset_more_sources">Mais fontes</string>
<string name="carset_hide">Ocultar</string> <string name="carset_hide">Ocultar</string>
<string name="carset_show">Mostrar</string> <string name="carset_show">Mostrar</string>
<string name="carset_phone">Celular</string> <string name="carset_phone">Celular</string>
@@ -188,8 +188,6 @@
<string name="car_right">Dreapta</string> <string name="car_right">Dreapta</string>
<string name="car_left">Stânga</string> <string name="car_left">Stânga</string>
<string name="car_autoplay">Redare automată la conectare</string> <string name="car_autoplay">Redare automată la conectare</string>
<string name="car_more_lyrics">Alte surse de versuri</string>
<string name="car_more_lyrics_note">De asemenea, interoghează BetterLyrics și KuGou, care obțin apoi numele melodiei și al artistului. Opțiunea „Oprit” interoghează doar LRCLIB.</string>
<string name="car_spoken_directions">Instrucțiuni vocale</string> <string name="car_spoken_directions">Instrucțiuni vocale</string>
<string name="car_spoken_directions_note">Dezactivează dacă vocea aplicației de hărți se aude deja în mașină.</string> <string name="car_spoken_directions_note">Dezactivează dacă vocea aplicației de hărți se aude deja în mașină.</string>
<string name="car_phone_speech">Folosește funcția de recunoaștere vocală a telefonului</string> <string name="car_phone_speech">Folosește funcția de recunoaștere vocală a telefonului</string>
@@ -378,8 +376,6 @@
<string name="carset_fit">Ajustare la ecran</string> <string name="carset_fit">Ajustare la ecran</string>
<string name="off">Oprit</string> <string name="off">Oprit</string>
<string name="on">Pornit</string> <string name="on">Pornit</string>
<string name="carset_lyrics_from">Versuri din</string>
<string name="carset_more_sources">Alte surse</string>
<string name="carset_hide">Ascunde</string> <string name="carset_hide">Ascunde</string>
<string name="carset_show">Afișează</string> <string name="carset_show">Afișează</string>
<string name="carset_phone">Telefon</string> <string name="carset_phone">Telefon</string>
@@ -200,8 +200,6 @@
<string name="car_right">Справа</string> <string name="car_right">Справа</string>
<string name="car_left">Слева</string> <string name="car_left">Слева</string>
<string name="car_autoplay">Воспроизводить при подключении</string> <string name="car_autoplay">Воспроизводить при подключении</string>
<string name="car_more_lyrics">Больше источников текстов</string>
<string name="car_more_lyrics_note">Также спрашивает BetterLyrics и KuGou, которые тогда получают название и исполнителя песни. Если выключено, спрашивает только LRCLIB.</string>
<string name="car_spoken_directions">Голосовые подсказки</string> <string name="car_spoken_directions">Голосовые подсказки</string>
<string name="car_spoken_directions_note">Выключите, если голос вашего навигатора уже звучит в машине.</string> <string name="car_spoken_directions_note">Выключите, если голос вашего навигатора уже звучит в машине.</string>
<string name="car_phone_speech">Распознавание речи телефона</string> <string name="car_phone_speech">Распознавание речи телефона</string>
@@ -431,8 +429,6 @@
<string name="carset_fit">Подогнать под экран</string> <string name="carset_fit">Подогнать под экран</string>
<string name="off">Выкл.</string> <string name="off">Выкл.</string>
<string name="on">Вкл.</string> <string name="on">Вкл.</string>
<string name="carset_lyrics_from">Тексты из</string>
<string name="carset_more_sources">Больше источников</string>
<string name="carset_hide">Скрыть</string> <string name="carset_hide">Скрыть</string>
<string name="carset_show">Показать</string> <string name="carset_show">Показать</string>
<string name="carset_phone">Телефон</string> <string name="carset_phone">Телефон</string>
@@ -200,8 +200,6 @@
<string name="car_right">Höger</string> <string name="car_right">Höger</string>
<string name="car_left">Vänster</string> <string name="car_left">Vänster</string>
<string name="car_autoplay">Spela upp vid anslutning</string> <string name="car_autoplay">Spela upp vid anslutning</string>
<string name="car_more_lyrics">Fler källor för låttexter</string>
<string name="car_more_lyrics_note">Frågar även BetterLyrics och KuGou, som då får låtens namn och artist. Av frågar bara LRCLIB.</string>
<string name="car_spoken_directions">Röstvägledning</string> <string name="car_spoken_directions">Röstvägledning</string>
<string name="car_spoken_directions_note">Stäng av den om kartappens egen röst redan hörs i bilen.</string> <string name="car_spoken_directions_note">Stäng av den om kartappens egen röst redan hörs i bilen.</string>
<string name="car_phone_speech">Använd telefonens taligenkänning</string> <string name="car_phone_speech">Använd telefonens taligenkänning</string>
@@ -429,8 +427,6 @@
<string name="carset_fit">Anpassa till skärmen</string> <string name="carset_fit">Anpassa till skärmen</string>
<string name="off">Av</string> <string name="off">Av</string>
<string name="on">På</string> <string name="on">På</string>
<string name="carset_lyrics_from">Låttexter från</string>
<string name="carset_more_sources">Fler källor</string>
<string name="carset_hide">Dölj</string> <string name="carset_hide">Dölj</string>
<string name="carset_show">Visa</string> <string name="carset_show">Visa</string>
<string name="carset_phone">Telefon</string> <string name="carset_phone">Telefon</string>
@@ -200,8 +200,6 @@
<string name="car_right">Sağ</string> <string name="car_right">Sağ</string>
<string name="car_left">Sol</string> <string name="car_left">Sol</string>
<string name="car_autoplay">Bağlanınca çal</string> <string name="car_autoplay">Bağlanınca çal</string>
<string name="car_more_lyrics">Daha fazla şarkı sözü kaynağı</string>
<string name="car_more_lyrics_note">BetterLyrics ve KuGou’ya da sorar; bunlar şarkının adını ve sanatçısını alır. Kapalıyken yalnızca LRCLIB’ye sorar.</string>
<string name="car_spoken_directions">Sesli yol tarifi</string> <string name="car_spoken_directions">Sesli yol tarifi</string>
<string name="car_spoken_directions_note">Harita uygulamanızın kendi sesi araçta zaten çalıyorsa kapatın.</string> <string name="car_spoken_directions_note">Harita uygulamanızın kendi sesi araçta zaten çalıyorsa kapatın.</string>
<string name="car_phone_speech">Telefonun konuşma tanımasını kullan</string> <string name="car_phone_speech">Telefonun konuşma tanımasını kullan</string>
@@ -429,8 +427,6 @@
<string name="carset_fit">Ekrana sığdır</string> <string name="carset_fit">Ekrana sığdır</string>
<string name="off">Kapalı</string> <string name="off">Kapalı</string>
<string name="on">Açık</string> <string name="on">Açık</string>
<string name="carset_lyrics_from">Sözlerin kaynağı</string>
<string name="carset_more_sources">Daha fazla kaynak</string>
<string name="carset_hide">Gizle</string> <string name="carset_hide">Gizle</string>
<string name="carset_show">Göster</string> <string name="carset_show">Göster</string>
<string name="carset_phone">Telefon</string> <string name="carset_phone">Telefon</string>
@@ -200,8 +200,6 @@
<string name="car_right">Праворуч</string> <string name="car_right">Праворуч</string>
<string name="car_left">Ліворуч</string> <string name="car_left">Ліворуч</string>
<string name="car_autoplay">Відтворювати під час підключення</string> <string name="car_autoplay">Відтворювати під час підключення</string>
<string name="car_more_lyrics">Більше джерел текстів</string>
<string name="car_more_lyrics_note">Також питає BetterLyrics і KuGou, які тоді отримують назву й виконавця пісні. Якщо вимкнено, питає лише LRCLIB.</string>
<string name="car_spoken_directions">Голосові підказки</string> <string name="car_spoken_directions">Голосові підказки</string>
<string name="car_spoken_directions_note">Вимкніть, якщо голос вашого навігатора вже звучить в авто.</string> <string name="car_spoken_directions_note">Вимкніть, якщо голос вашого навігатора вже звучить в авто.</string>
<string name="car_phone_speech">Розпізнавання мовлення телефона</string> <string name="car_phone_speech">Розпізнавання мовлення телефона</string>
@@ -431,8 +429,6 @@
<string name="carset_fit">Припасувати до екрана</string> <string name="carset_fit">Припасувати до екрана</string>
<string name="off">Вимк.</string> <string name="off">Вимк.</string>
<string name="on">Увімк.</string> <string name="on">Увімк.</string>
<string name="carset_lyrics_from">Тексти з</string>
<string name="carset_more_sources">Більше джерел</string>
<string name="carset_hide">Сховати</string> <string name="carset_hide">Сховати</string>
<string name="carset_show">Показати</string> <string name="carset_show">Показати</string>
<string name="carset_phone">Телефон</string> <string name="carset_phone">Телефон</string>
@@ -200,8 +200,6 @@
<string name="car_right">右</string> <string name="car_right">右</string>
<string name="car_left">左</string> <string name="car_left">左</string>
<string name="car_autoplay">连接时自动播放</string> <string name="car_autoplay">连接时自动播放</string>
<string name="car_more_lyrics">更多歌词来源</string>
<string name="car_more_lyrics_note">同时查询 BetterLyrics 和 KuGou,它们会收到歌曲名和歌手。关闭后只查询 LRCLIB。</string>
<string name="car_spoken_directions">语音导航</string> <string name="car_spoken_directions">语音导航</string>
<string name="car_spoken_directions_note">如果地图应用自己的语音已经在车上播放,请关闭此项。</string> <string name="car_spoken_directions_note">如果地图应用自己的语音已经在车上播放,请关闭此项。</string>
<string name="car_phone_speech">使用手机的语音识别</string> <string name="car_phone_speech">使用手机的语音识别</string>
@@ -428,8 +426,6 @@
<string name="carset_fit">适应屏幕</string> <string name="carset_fit">适应屏幕</string>
<string name="off">关</string> <string name="off">关</string>
<string name="on">开</string> <string name="on">开</string>
<string name="carset_lyrics_from">歌词来源</string>
<string name="carset_more_sources">更多来源</string>
<string name="carset_hide">隐藏</string> <string name="carset_hide">隐藏</string>
<string name="carset_show">显示</string> <string name="carset_show">显示</string>
<string name="carset_phone">手机</string> <string name="carset_phone">手机</string>
+6 -4
View File
@@ -206,11 +206,11 @@
<string name="car_skip_buttons">Skip buttons</string> <string name="car_skip_buttons">Skip buttons</string>
<string name="car_song_title">Song title</string> <string name="car_song_title">Song title</string>
<string name="car_player_beside_map">Player beside the map</string> <string name="car_player_beside_map">Player beside the map</string>
<string name="car_player_beside_map_note">The side of the map the player docks to when you tap its dock button.</string>
<string name="car_right">Right</string> <string name="car_right">Right</string>
<string name="car_left">Left</string> <string name="car_left">Left</string>
<string name="car_autoplay">Autoplay on connect</string> <string name="car_autoplay">Autoplay on connect</string>
<string name="car_more_lyrics">More lyrics sources</string> <string name="car_autoplay_note">Picks the music up where it stopped once the car connects.</string>
<string name="car_more_lyrics_note">Also asks BetterLyrics and KuGou, which then get the song\'s name and artist. Off asks LRCLIB only.</string>
<string name="car_spoken_directions">Spoken directions</string> <string name="car_spoken_directions">Spoken directions</string>
<string name="car_spoken_directions_note">Turn it off if your map app\'s own voice already plays in the car.</string> <string name="car_spoken_directions_note">Turn it off if your map app\'s own voice already plays in the car.</string>
<string name="car_phone_speech">Use the phone\'s speech recognition</string> <string name="car_phone_speech">Use the phone\'s speech recognition</string>
@@ -218,6 +218,10 @@
<string name="car_warn_bluetooth">Warn when Bluetooth isn\'t connected</string> <string name="car_warn_bluetooth">Warn when Bluetooth isn\'t connected</string>
<string name="car_warn_location">Warn when location is off</string> <string name="car_warn_location">Warn when location is off</string>
<string name="car_experimental">Experimental features</string> <string name="car_experimental">Experimental features</string>
<string name="car_experimental_note">Features still in testing. They may not work in every car.</string>
<string name="carset_experimental_heading">Experimental</string>
<string name="carset_advanced">Advanced</string>
<string name="carset_fit_needs_car">Connect to a car to save this fit for it.</string>
<string name="car_vehicle_data">Vehicle data</string> <string name="car_vehicle_data">Vehicle data</string>
<string name="car_vehicle_data_note">Adds a Vehicle data tile: a live dump of the car\'s sensors. The field names are guesses until you\'ve watched one change.</string> <string name="car_vehicle_data_note">Adds a Vehicle data tile: a live dump of the car\'s sensors. The field names are guesses until you\'ve watched one change.</string>
<string name="car_adaptive_video">Adaptive video quality</string> <string name="car_adaptive_video">Adaptive video quality</string>
@@ -462,8 +466,6 @@
<string name="carset_fit">Fit to the screen</string> <string name="carset_fit">Fit to the screen</string>
<string name="off">Off</string> <string name="off">Off</string>
<string name="on">On</string> <string name="on">On</string>
<string name="carset_lyrics_from">Lyrics from</string>
<string name="carset_more_sources">More sources</string>
<string name="carset_hide">Hide</string> <string name="carset_hide">Hide</string>
<string name="carset_show">Show</string> <string name="carset_show">Show</string>
<string name="carset_phone">Phone</string> <string name="carset_phone">Phone</string>