usage notes changes, audio changes, video quality increase.

This commit is contained in:
Seb3thehacker committed 2026-10-07 23:50:47 -07:00
1 parent 55320c4d98
commit c212c2e2d1
4 files changed
+83 -35

No files matched your search

@@ -60,6 +60,8 @@ fun GearslipApp(onDisconnect: () -> Unit, onRequestCallScreening: () -> Unit) {
},
)
}
// Home's usage notes row opens Settings at that section rather than at the top.
var settingsAtUsageNotes by rememberSaveable { mutableStateOf(false) }
BackHandler(enabled = screen != Screen.HOME) {
screen = if (screen == Screen.RECOMMENDED_APPS) Screen.SETTINGS else Screen.HOME
}
@@ -95,18 +97,20 @@ fun GearslipApp(onDisconnect: () -> Unit, onRequestCallScreening: () -> Unit) {
when (screen) {
Screen.HOME -> HomeScreen(
onOpenLogs = { screen = Screen.LOGS },
onOpenSettings = { screen = Screen.SETTINGS },
onOpenSettings = { settingsAtUsageNotes = false; screen = Screen.SETTINGS },
onOpenCarPreview = { screen = Screen.CAR_PREVIEW },
onOpenHelp = { screen = Screen.HELP },
onOpenUsageNotes = { settingsAtUsageNotes = true; screen = Screen.SETTINGS },
onDisconnect = onDisconnect,
)
Screen.WHATS_NEW -> WhatsNewScreen(onBack = { screen = Screen.SETTINGS })
Screen.LOGS -> LogsScreen(onBack = { screen = Screen.HOME })
Screen.SETTINGS -> SettingsScreen(
onBack = { screen = Screen.HOME },
onOpenRecommendedApps = { screen = Screen.RECOMMENDED_APPS },
onOpenWhatsNew = { screen = Screen.WHATS_NEW },
onOpenRecommendedApps = { settingsAtUsageNotes = false; screen = Screen.RECOMMENDED_APPS },
onOpenWhatsNew = { settingsAtUsageNotes = false; screen = Screen.WHATS_NEW },
onReplayTutorial = { screen = Screen.SETUP_GUIDE },
atUsageNotes = settingsAtUsageNotes,
)
Screen.RECOMMENDED_APPS -> RecommendedAppsScreen(onBack = { screen = Screen.SETTINGS })
Screen.CAR_PREVIEW -> CarPreviewScreen(onBack = { screen = Screen.HOME })
@@ -48,6 +48,7 @@ fun HomeScreen(
onOpenSettings: () -> Unit,
onOpenCarPreview: () -> Unit,
onOpenHelp: () -> Unit,
onOpenUsageNotes: () -> Unit,
onDisconnect: () -> Unit,
) {
val status by SessionStatus.state.collectAsStateWithLifecycle()
@@ -92,7 +93,6 @@ fun HomeScreen(
StatusCard(status)
Spacer(Modifier.height(16.dp))
UpdateCard()
UsageNotesHomeCard()
}
// Sits at the bottom, where a thumb reaches it.
@@ -106,6 +106,9 @@ fun HomeScreen(
Spacer(Modifier.height(16.dp))
}
UsageNotesHomeCard(onOpen = onOpenUsageNotes)
Spacer(Modifier.height(16.dp))
Surface(color = MaterialTheme.colorScheme.surfaceContainerHigh, shape = MaterialTheme.shapes.extraLarge) {
Column {
if (!live) {
@@ -57,6 +57,9 @@ import app.seb3thehacker.gearslip.BuildConfig
import app.seb3thehacker.gearslip.CertProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.snapshotFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterial3Api::class)
@@ -66,7 +69,16 @@ fun SettingsScreen(
onOpenRecommendedApps: () -> Unit,
onOpenWhatsNew: () -> Unit,
onReplayTutorial: () -> Unit,
atUsageNotes: Boolean = false,
) {
val scroll = rememberScrollState()
// Opened from Home's usage notes row: usage notes is the last section, so start at the end.
if (atUsageNotes) {
LaunchedEffect(Unit) {
snapshotFlow { scroll.maxValue }.first { it > 0 && it < Int.MAX_VALUE }
scroll.scrollTo(scroll.maxValue)
}
}
Scaffold(
containerColor = MaterialTheme.colorScheme.background,
topBar = {
@@ -85,7 +97,7 @@ fun SettingsScreen(
.fillMaxSize()
.padding(padding)
.padding(horizontal = 24.dp)
.verticalScroll(rememberScrollState()),
.verticalScroll(scroll),
verticalArrangement = Arrangement.spacedBy(24.dp),
) {
Spacer(Modifier.height(0.dp))
@@ -1,6 +1,7 @@
package app.seb3thehacker.gearslip.ui
import androidx.compose.animation.AnimatedVisibility
import android.content.Context
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
@@ -9,6 +10,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.selection.toggleable
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
@@ -83,11 +86,7 @@ internal fun ColumnScope.UsageStatsRows() {
SettingsRow(
"Send usage notes",
Modifier.toggleable(on, role = Role.Switch) { on = it; save() },
subtitle = when {
BuildConfig.DEBUG -> "Dev build: nothing is ever sent"
BuildConfig.STATS_URL.isBlank() -> "This build has no stats server, so it sends nothing"
else -> null
},
subtitle = buildNote(),
icon = Icons.Filled.Send,
trailing = { Switch(checked = on, onCheckedChange = null) },
)
@@ -126,14 +125,7 @@ internal fun ColumnScope.UsageStatsRows() {
)
AnimatedVisibility(noteOpen) {
// Rebuilt from the saved choices each time one changes, so it always matches.
val note = remember(choices) {
val preview = UsageStats.preview(context, everything = true)
// Until the first drive the list is empty, so show what one entry looks like.
val example = if (preview.optJSONArray("sessions")?.length() == 0) {
"\n\nExample, not sent: one drive adds this to \"sessions\"\n" + UsageStats.exampleSession.toString(2)
} else ""
preview.toString(2) + example
}
val note = remember(choices) { notePreview(context) }
Surface(
color = MaterialTheme.colorScheme.surfaceContainerLowest,
shape = MaterialTheme.shapes.medium,
@@ -164,23 +156,60 @@ private fun Choice(label: String, checked: Boolean, onChange: (Boolean) -> Unit)
}
}
/** Usage notes on Home, always: the switch, and once it's on, what to share. */
/** The note as it would go out now, with an example drive until a real one is in it. */
private fun notePreview(context: Context): String {
val preview = UsageStats.preview(context, everything = true)
// Until the first drive the list is empty, so show what one entry looks like.
val example = if (preview.optJSONArray("sessions")?.length() == 0) {
"\n\nExample, not sent: one drive adds this to \"sessions\"\n" + UsageStats.exampleSession.toString(2)
} else ""
return preview.toString(2) + example
}
/** Why this build sends nothing, when it doesn't; null for a normal release. */
private fun buildNote(): String? = when {
BuildConfig.DEBUG -> "Dev build: nothing is ever sent"
BuildConfig.STATS_URL.isBlank() -> "This build has no stats server, so it sends nothing"
else -> null
}
/**
* Usage notes on Home, one row with one action either way. While they're off, the row is the
* switch, so opting in is a single tap. Once they're on, it shrinks to a quiet line that opens
* Settings at its usage notes section, so a driver already sharing isn't pitched again.
*/
@Composable
internal fun UsageNotesHomeCard(modifier: Modifier = Modifier) {
Column(modifier) {
Text(
USAGE_STATS_TITLE,
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(start = 16.dp, bottom = 4.dp),
)
Text(
"Share a short note after each drive, so the next driver knows whether Gearslip works " +
"in their car. No name, account, or location.",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 12.dp),
)
SettingsCard { UsageStatsRows() }
internal fun UsageNotesHomeCard(onOpen: () -> Unit, modifier: Modifier = Modifier) {
val context = LocalContext.current
var on by remember { mutableStateOf(UsageStats.enabled(context)) }
SettingsCard(modifier) {
if (on) {
SettingsRow(
"Sharing usage notes",
Modifier.clickable(onClick = onOpen),
subtitle = buildNote(),
icon = Icons.Filled.Check,
trailing = {
Icon(
Icons.AutoMirrored.Filled.KeyboardArrowRight,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
},
)
} else {
SettingsRow(
USAGE_STATS_TITLE,
Modifier.toggleable(false, role = Role.Switch) {
UsageStats.set(context, true, UsageStats.choices(context))
on = true
},
subtitle = buildNote() ?: ("A short note after each drive tells the next driver " +
"whether their car works. No name, account, or location"),
icon = Icons.Filled.Send,
trailing = { Switch(checked = false, onCheckedChange = null) },
)
}
}
}