mirror of
https://github.com/twinksoftware/Gearslip.git
synced 2026-11-11 00:12:24 +01:00
phone: car settings all live under Home's car groups, with voice models, car audio and screen size moved in and a new More group
This commit is contained in:
1 parent
1f9ef552c1
commit
15d6721705
2 files changed
+89
-29
No files matched your search
@@ -2,6 +2,7 @@ package app.seb3thehacker.gearslip.ui
|
|||||||
|
|
||||||
import app.seb3thehacker.gearslip.car.theme.DarkStyle
|
import app.seb3thehacker.gearslip.car.theme.DarkStyle
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.selection.selectable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -43,6 +44,7 @@ enum class CarGroup(val title: String) {
|
|||||||
NAV_BAR("Nav bar and player"),
|
NAV_BAR("Nav bar and player"),
|
||||||
SOUND("Sound and voice"),
|
SOUND("Sound and voice"),
|
||||||
WARNINGS("Warnings"),
|
WARNINGS("Warnings"),
|
||||||
|
MORE("More"),
|
||||||
}
|
}
|
||||||
|
|
||||||
private val sizeChoices = listOf(1f to "Normal", 1.25f to "Large", 1.5f to "Extra large")
|
private val sizeChoices = listOf(1f to "Normal", 1.25f to "Large", 1.5f to "Extra large")
|
||||||
@@ -78,6 +80,7 @@ internal fun HomeCarSettings(onOpen: (CarGroup) -> Unit, modifier: Modifier = Mo
|
|||||||
val locationWarning by CarSettings.locationWarning.collectAsState()
|
val locationWarning by CarSettings.locationWarning.collectAsState()
|
||||||
val playerLeft by CarSettings.playerLeft.collectAsState()
|
val playerLeft by CarSettings.playerLeft.collectAsState()
|
||||||
val theme by CarSettings.appTheme.collectAsState()
|
val theme by CarSettings.appTheme.collectAsState()
|
||||||
|
val experimental by CarSettings.experimentalFeaturesEnabled.collectAsState()
|
||||||
|
|
||||||
fun summary(group: CarGroup) = when (group) {
|
fun summary(group: CarGroup) = when (group) {
|
||||||
CarGroup.SCREEN -> listOf(openChoices().label(open), sizeChoices.label(scale), themeChoices.label(theme))
|
CarGroup.SCREEN -> listOf(openChoices().label(open), sizeChoices.label(scale), themeChoices.label(theme))
|
||||||
@@ -87,6 +90,7 @@ internal fun HomeCarSettings(onOpen: (CarGroup) -> Unit, modifier: Modifier = Mo
|
|||||||
"Bluetooth ${if (bluetoothWarning && !pipeAudio) "on" else "off"}",
|
"Bluetooth ${if (bluetoothWarning && !pipeAudio) "on" else "off"}",
|
||||||
"Location ${if (locationWarning) "on" else "off"}",
|
"Location ${if (locationWarning) "on" else "off"}",
|
||||||
)
|
)
|
||||||
|
CarGroup.MORE -> listOf("Web page", "Experimental features ${if (experimental) "shown" else "hidden"}")
|
||||||
}.filter { it.isNotEmpty() }.joinToString(" · ")
|
}.filter { it.isNotEmpty() }.joinToString(" · ")
|
||||||
|
|
||||||
Column(modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
Column(modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
@@ -134,6 +138,16 @@ internal fun CarGroupScreen(group: CarGroup, onBack: () -> Unit) {
|
|||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
) {
|
) {
|
||||||
SettingsCard { GroupRows(group) }
|
SettingsCard { GroupRows(group) }
|
||||||
|
// The bigger pieces each keep their own card: the car screen's size and the preview's,
|
||||||
|
// and the two voice models with their downloads.
|
||||||
|
when (group) {
|
||||||
|
CarGroup.SCREEN -> CarScreenSection()
|
||||||
|
CarGroup.SOUND -> {
|
||||||
|
CarAudioSection()
|
||||||
|
VoiceModelSection()
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
Text(
|
Text(
|
||||||
"These are the same settings as Settings in the car. A change here shows there too.",
|
"These are the same settings as Settings in the car. A change here shows there too.",
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
@@ -163,6 +177,9 @@ private fun GroupRows(group: CarGroup) {
|
|||||||
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()
|
||||||
|
val experimental by CarSettings.experimentalFeaturesEnabled.collectAsState()
|
||||||
|
val carSensors by CarSettings.carSensorsEnabled.collectAsState()
|
||||||
|
val adaptiveVideo by CarSettings.adaptiveVideoEnabled.collectAsState()
|
||||||
|
|
||||||
when (group) {
|
when (group) {
|
||||||
CarGroup.SCREEN -> {
|
CarGroup.SCREEN -> {
|
||||||
@@ -199,8 +216,6 @@ private fun GroupRows(group: CarGroup) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
CarGroup.SOUND -> {
|
CarGroup.SOUND -> {
|
||||||
Choice("Car audio", listOf(false to "Bluetooth", true to "USB"), pipeAudio, CarSettings::setPipeAudio)
|
|
||||||
SettingsDivider()
|
|
||||||
Toggle(
|
Toggle(
|
||||||
"Spoken directions",
|
"Spoken directions",
|
||||||
speakDirections,
|
speakDirections,
|
||||||
@@ -222,6 +237,27 @@ private fun GroupRows(group: CarGroup) {
|
|||||||
}
|
}
|
||||||
Toggle("Warn when location is off", locationWarning, CarSettings::setLocationWarning)
|
Toggle("Warn when location is off", locationWarning, CarSettings::setLocationWarning)
|
||||||
}
|
}
|
||||||
|
CarGroup.MORE -> {
|
||||||
|
WebPageRow()
|
||||||
|
SettingsDivider()
|
||||||
|
Toggle("Experimental features", experimental, CarSettings::setExperimentalFeaturesEnabled)
|
||||||
|
if (experimental) {
|
||||||
|
SettingsDivider()
|
||||||
|
Toggle(
|
||||||
|
"Vehicle data",
|
||||||
|
carSensors,
|
||||||
|
CarSettings::setCarSensorsEnabled,
|
||||||
|
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.",
|
||||||
|
)
|
||||||
|
SettingsDivider()
|
||||||
|
Toggle(
|
||||||
|
"Adaptive video quality",
|
||||||
|
adaptiveVideo,
|
||||||
|
CarSettings::setAdaptiveVideoEnabled,
|
||||||
|
note = "Lowers the picture's quality when the car or its Wi-Fi can't keep up, and raises it again once it can. Applies on the next connection.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,3 +299,54 @@ private fun Toggle(title: String, on: Boolean, onChange: (Boolean) -> Unit, note
|
|||||||
trailing = { Switch(checked = on, onCheckedChange = null) },
|
trailing = { Switch(checked = on, onCheckedChange = null) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The page the car's Browser app opens, typed here instead of on the car's keyboard. */
|
||||||
|
@Composable
|
||||||
|
private fun WebPageRow() {
|
||||||
|
val context = androidx.compose.ui.platform.LocalContext.current
|
||||||
|
var url by remember { mutableStateOf(app.seb3thehacker.gearslip.AppSettings.startupUrl(context)) }
|
||||||
|
Column(Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 12.dp)) {
|
||||||
|
androidx.compose.material3.OutlinedTextField(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
url = it
|
||||||
|
app.seb3thehacker.gearslip.AppSettings.setStartupUrl(context, it)
|
||||||
|
},
|
||||||
|
label = { Text("Web page") },
|
||||||
|
placeholder = { Text("DuckDuckGo") },
|
||||||
|
singleLine = true,
|
||||||
|
keyboardOptions = androidx.compose.foundation.text.KeyboardOptions(keyboardType = androidx.compose.ui.text.input.KeyboardType.Uri),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
"Shown by the Browser app in the car. Empty opens DuckDuckGo. Applies on the next connection.",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(top = 6.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** How music reaches the car: by Bluetooth as usual, or through Gearslip over USB. Same choice as setup. */
|
||||||
|
@Composable
|
||||||
|
internal fun CarAudioSection() {
|
||||||
|
val usb by CarSettings.pipeAudio.collectAsState()
|
||||||
|
SettingsSection(
|
||||||
|
"Car audio",
|
||||||
|
footer = if (usb) "Run the command in Help once, and USB stops asking." else "Calls go over Bluetooth either way.",
|
||||||
|
) {
|
||||||
|
SettingsRow(
|
||||||
|
"Bluetooth (Recommended)",
|
||||||
|
Modifier.selectable(!usb, role = androidx.compose.ui.semantics.Role.RadioButton) { CarSettings.setPipeAudio(false) },
|
||||||
|
subtitle = "No prompt each drive, and your map app's voice reaches the car",
|
||||||
|
trailing = { androidx.compose.material3.RadioButton(selected = !usb, onClick = null) },
|
||||||
|
)
|
||||||
|
SettingsDivider()
|
||||||
|
SettingsRow(
|
||||||
|
"USB",
|
||||||
|
Modifier.selectable(usb, role = androidx.compose.ui.semantics.Role.RadioButton) { CarSettings.setPipeAudio(true) },
|
||||||
|
subtitle = "Clearer sound with no delay. Each drive, Android asks you to share your screen, which lets Gearslip send the sound",
|
||||||
|
trailing = { androidx.compose.material3.RadioButton(selected = usb, onClick = null) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -140,9 +140,6 @@ fun SettingsScreen(
|
|||||||
footer = "Gearslip is free, with no ads and no tracking. Your support keeps it going.",
|
footer = "Gearslip is free, with no ads and no tracking. Your support keeps it going.",
|
||||||
) { DonateRows() }
|
) { DonateRows() }
|
||||||
if (ready(2)) ConnectionSection()
|
if (ready(2)) ConnectionSection()
|
||||||
if (ready(3)) CarAudioSection()
|
|
||||||
if (ready(4)) CarScreenSection()
|
|
||||||
if (ready(5)) VoiceModelSection()
|
|
||||||
if (SHOW_RECOMMENDED_APPS && ready(6)) AppsSection(onOpenRecommendedApps)
|
if (SHOW_RECOMMENDED_APPS && ready(6)) AppsSection(onOpenRecommendedApps)
|
||||||
if (ready(7)) SettingsSection(
|
if (ready(7)) SettingsSection(
|
||||||
"Usage notes",
|
"Usage notes",
|
||||||
@@ -225,30 +222,6 @@ private fun DebugSection() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** How music reaches the car: by Bluetooth as usual, or through Gearslip over USB. Same choice as setup. */
|
|
||||||
@Composable
|
|
||||||
private fun CarAudioSection() {
|
|
||||||
val usb by CarSettings.pipeAudio.collectAsState()
|
|
||||||
SettingsSection(
|
|
||||||
"Car audio",
|
|
||||||
footer = if (usb) "Run the command in Help once, and USB stops asking." else "Calls go over Bluetooth either way.",
|
|
||||||
) {
|
|
||||||
SettingsRow(
|
|
||||||
"Bluetooth (Recommended)",
|
|
||||||
Modifier.selectable(!usb, role = Role.RadioButton) { CarSettings.setPipeAudio(false) },
|
|
||||||
subtitle = "No prompt each drive, and your map app's voice reaches the car",
|
|
||||||
trailing = { RadioButton(selected = !usb, onClick = null) },
|
|
||||||
)
|
|
||||||
SettingsDivider()
|
|
||||||
SettingsRow(
|
|
||||||
"USB",
|
|
||||||
Modifier.selectable(usb, role = Role.RadioButton) { CarSettings.setPipeAudio(true) },
|
|
||||||
subtitle = "Clearer sound with no delay. Each drive, Android asks you to share your screen, which lets Gearslip send the sound",
|
|
||||||
trailing = { RadioButton(selected = usb, onClick = null) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun AppsSection(onOpenRecommendedApps: () -> Unit) {
|
private fun AppsSection(onOpenRecommendedApps: () -> Unit) {
|
||||||
SettingsSection("Apps") {
|
SettingsSection("Apps") {
|
||||||
|
|||||||
Reference in new issue
Block a user