The car audio choice moves to Settings, and says map apps' spoken directions stay on the phone with Through Gearslip

The phone's ID in the usage note can be selected and copied, for a request to see or
delete what was sent.
This commit is contained in:
Seb3thehacker committed 2026-11-11 11:29:22 -08:00
1 parent f10ffa4142
commit 0ca828de4a
4 files changed
+52 -38

No files matched your search

@@ -109,8 +109,6 @@ fun HomeScreen(
) {
Spacer(Modifier.height(16.dp))
StatusCard(status)
Spacer(Modifier.height(16.dp))
AudioSwitch()
if (live) BluetoothMissingCard()
if (live) BackgroundLocationCard()
BluetoothAddressCard()
@@ -315,34 +313,6 @@ private fun Arrow() = Icon(
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
/**
* Where the car's sound comes from, switchable in one tap from Home: Bluetooth, or through
* Gearslip over the car link. It takes effect at once when a car is connected.
*/
@Composable
private fun AudioSwitch() {
val pipe by app.seb3thehacker.gearslip.car.CarSettings.pipeAudio.collectAsState()
val thisCar by app.seb3thehacker.gearslip.car.CarSettings.thisCarPipes.collectAsState()
val through = pipe || thisCar
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(
stringResource(R.string.car_audio),
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(start = 4.dp),
)
androidx.compose.material3.SingleChoiceSegmentedButtonRow(Modifier.fillMaxWidth()) {
listOf(false to R.string.guide_audio_bluetooth, true to R.string.guide_audio_usb).forEachIndexed { i, (value, label) ->
SegmentedButton(
selected = through == value,
onClick = { app.seb3thehacker.gearslip.car.CarSettings.setPipeAudio(value) },
shape = androidx.compose.material3.SegmentedButtonDefaults.itemShape(i, 2),
) { Text(stringResource(label), maxLines = 1) }
}
}
}
}
@Composable
private fun StatusCard(status: SessionStatus.Snapshot) {
val scheme = MaterialTheme.colorScheme
@@ -1,5 +1,8 @@
package app.seb3thehacker.gearslip.ui
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.selected
import androidx.compose.material.icons.filled.Create
import androidx.compose.ui.res.stringResource
@@ -250,6 +253,7 @@ private fun ConnectionSection() {
icon = Icons.Filled.Home,
trailing = { Switch(checked = launcher, onCheckedChange = null) },
)
CarAudioSetting()
var address by remember { mutableStateOf(CarBluetooth.typedAddress(context)) }
var editing by remember { mutableStateOf(false) }
SettingsRow(
@@ -275,6 +279,42 @@ private fun ConnectionSection() {
}
}
/**
* Where the car's sound comes from: Bluetooth, or through Gearslip over the car link. Takes
* effect at once when a car is connected. The chosen pill is filled in the main colour, the
* other tonal, like the buttons on Home.
*/
@Composable
private fun CarAudioSetting() {
val pipe by app.seb3thehacker.gearslip.car.CarSettings.pipeAudio.collectAsState()
val thisCar by app.seb3thehacker.gearslip.car.CarSettings.thisCarPipes.collectAsState()
val through = pipe || thisCar
val scheme = MaterialTheme.colorScheme
// Android won't let Gearslip capture navigation voices, only media, so they stay on the phone.
SettingsRow(
stringResource(R.string.car_audio),
subtitle = if (through) stringResource(R.string.home_audio_usb_warning) else null,
icon = app.seb3thehacker.gearslip.car.MediaIcons.MusicNote,
)
androidx.compose.foundation.layout.Row(
Modifier.fillMaxWidth().padding(start = 56.dp, end = 16.dp, bottom = 12.dp),
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(8.dp),
) {
listOf(false to R.string.guide_audio_bluetooth, true to R.string.guide_audio_usb).forEach { (value, label) ->
val chosen = through == value
androidx.compose.material3.Button(
onClick = { app.seb3thehacker.gearslip.car.CarSettings.setPipeAudio(value) },
colors = androidx.compose.material3.ButtonDefaults.buttonColors(
containerColor = if (chosen) scheme.primary else scheme.secondaryContainer,
contentColor = if (chosen) scheme.onPrimary else scheme.onSecondaryContainer,
),
contentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 12.dp),
modifier = Modifier.weight(1f).height(48.dp).semantics { selected = chosen },
) { Text(stringResource(label), maxLines = 1) }
}
}
}
/** The phone's Bluetooth address, typed in by hand: Android doesn't let apps read it. */
@Composable
internal fun AddressDialog(initial: String, onDismiss: () -> Unit, onSave: (String) -> Boolean) {
@@ -129,12 +129,15 @@ internal fun ColumnScope.UsageStatsRows() {
shape = MaterialTheme.shapes.medium,
modifier = Modifier.fillMaxWidth().padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
) {
Text(
note,
style = MaterialTheme.typography.bodySmall,
fontFamily = FontFamily.Monospace,
modifier = Modifier.padding(12.dp),
)
// Selectable, so the phone's "id" can be copied into a request to see or delete its notes.
androidx.compose.foundation.text.selection.SelectionContainer {
Text(
note,
style = MaterialTheme.typography.bodySmall,
fontFamily = FontFamily.Monospace,
modifier = Modifier.padding(12.dp),
)
}
}
}
}