certificate: say where it comes from, offer import alongside download, keep cert files out of the repo

This commit is contained in:
Seb3thehacker committed 2026-10-08 15:25:04 -07:00
1 parent 246f571a86
commit a549077c13
4 files changed
+39 -18

No files matched your search

+8 -1
View File
@@ -30,4 +30,11 @@ CLAUDE.md
# Other
archive/
.wrangler/
stats-worker/
stats-worker/
# Certificates and keys never go in the repo; the app downloads or imports them on the phone.
*.p12
*.pfx
*.pem
*.crt
*.key
+11
View File
@@ -118,6 +118,17 @@ keystore. Without them, the debug build still works.
[docs/BUILDING_APPS.md](docs/BUILDING_APPS.md) specifies how to write apps that Gearslip
can render: templated apps, media apps, and the Gearslip app protocol.
## How Gearslip talks to cars
Gearslip is an independent program, written to work with car head units. What it knows of
the protocol comes from public open-source projects such as
[aasdk](https://github.com/opencardev/aasdk) and open-android-auto, and it contains none
of Google's Android Auto code.
Gearslip ships without a certificate. On first launch you choose where yours comes from:
download the one the aasdk project publishes, or import a file of your own. The
certificate stays on your phone. Gearslip never uploads, backs up, or logs it.
## Trademarks
Android, Android Auto, Google Messages, and Waze are trademarks of Google LLC. Spotify is
@@ -60,7 +60,6 @@ fun CertSetupScreen(onDone: () -> Unit) {
val scope = rememberCoroutineScope()
var downloadState by remember { mutableStateOf<DownloadState>(DownloadState.Idle) }
var showImport by remember { mutableStateOf(false) }
var pendingUri by remember { mutableStateOf<Uri?>(null) }
var manualProgress by remember { mutableStateOf(false) }
@@ -91,7 +90,7 @@ fun CertSetupScreen(onDone: () -> Unit) {
if (installed) {
"Cars accept only Google-issued certificates, and you have one loaded."
} else {
"Cars accept only Google-issued certificates. Download one to connect."
"Cars accept only Google-issued certificates. Gearslip doesn't include one, so choose where yours comes from."
},
style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center,
@@ -131,10 +130,11 @@ fun CertSetupScreen(onDone: () -> Unit) {
},
modifier = Modifier.fillMaxWidth().height(56.dp),
) {
Text("Download certificate")
Text("Download from aasdk")
}
Text(
"Works with most cars built before 2020. Newer cars reject it.",
"The open-source aasdk project publishes this certificate at github.com/opencardev/aasdk. " +
"Most cars accept it; some from around 2020 on don't.",
style = MaterialTheme.typography.bodySmall,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onSurfaceVariant,
@@ -177,16 +177,18 @@ fun CertSetupScreen(onDone: () -> Unit) {
Spacer(Modifier.height(16.dp))
if (showImport) {
OutlinedButton(
onClick = { importLauncher.launch(arrayOf("*/*")) },
modifier = Modifier.fillMaxWidth().height(52.dp),
) { Text("Import your own .p12 cert") }
Spacer(Modifier.height(16.dp))
} else {
TextButton(onClick = { showImport = true }) { Text("Import your own certificate instead") }
Spacer(Modifier.height(8.dp))
}
OutlinedButton(
onClick = { importLauncher.launch(arrayOf("*/*")) },
modifier = Modifier.fillMaxWidth().height(52.dp),
) { Text("Import my own file") }
Text(
"A .p12 file with its private key, from wherever you choose.",
style = MaterialTheme.typography.bodySmall,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 8.dp),
)
Spacer(Modifier.height(16.dp))
OutlinedButton(
onClick = {
@@ -208,7 +208,7 @@ private fun CertificateSection() {
}
SettingsDivider()
SettingsRow(
if (downloading) "Downloading…" else "Download certificate",
if (downloading) "Downloading…" else "Download from aasdk",
Modifier.clickable(enabled = !downloading) {
downloading = true
scope.launch {
@@ -222,13 +222,14 @@ private fun CertificateSection() {
}
}
},
subtitle = "The certificate the open-source aasdk project publishes on GitHub. Gearslip doesn't include one",
icon = Icons.Filled.Refresh,
)
SettingsDivider()
SettingsRow(
"Import a certificate",
"Import my own file",
Modifier.clickable { picker.launch(arrayOf("*/*")) },
subtitle = "A .p12 file with its private key",
subtitle = "A .p12 file with its private key, from wherever you choose",
icon = Icons.Filled.Add,
)
val removable = when (s?.kind) {