From 4c07adab7d2bee2ccce529959a368dbfd43ffd2e Mon Sep 17 00:00:00 2001 From: dirwin31 <83434411+dirwin31@users.noreply.github.com> Date: Mon, 26 Oct 2026 13:00:39 -0700 Subject: [PATCH] UI: Selector Fixes, Less Duplicate for AA Wireless. --- .../galaxy/tests/test_android_auto.mjs | 42 ++++++++----------- .../starpilot/galaxy/web/js/android-auto.js | 32 +++++++------- 2 files changed, 33 insertions(+), 41 deletions(-) diff --git a/openpilot/starpilot/galaxy/tests/test_android_auto.mjs b/openpilot/starpilot/galaxy/tests/test_android_auto.mjs index 20052e0479..8aa7ee1b45 100644 --- a/openpilot/starpilot/galaxy/tests/test_android_auto.mjs +++ b/openpilot/starpilot/galaxy/tests/test_android_auto.mjs @@ -1,3 +1,4 @@ +import { settingControl } from "../web/js/galaxy-setting-row.js" import assert from "node:assert/strict" import { readFileSync } from "node:fs" import { AndroidAutoFeed, AndroidAutoPage, validPairingStatus, validPromptInput, uploadPackage, uploadTimeout, @@ -18,7 +19,7 @@ assert.equal(validPromptInput({ kind: "passkey" }, "1234567"), false) assert.equal(validPromptInput(prompt, ""), true) assert.match(AndroidAutoPage.template, /Check that this code matches your car/) assert.match(AndroidAutoPage.template, /Cancel Search \/ Pairing/) -assert.match(AndroidAutoPage.template, /:row="wiredRow"[^>]*:disabled="wiredDisabled"/) +assert.doesNotMatch(AndroidAutoPage.template, /:row="wiredRow"/) assert.match(AndroidAutoPage.template, /Read the Android Auto troubleshooting guide/) assert.doesNotMatch(AndroidAutoPage.template, /v-html|autoaccept/i) const app = readFileSync(new URL("../web/js/app.js", import.meta.url), "utf8") @@ -160,32 +161,24 @@ function page(selectedSetup = setup) { for (const [key, method] of Object.entries(AndroidAutoPage.methods)) vm[key] = method.bind(vm) return vm } -// Wired setup is available, while the existing parked/session/ownership gates remain enforced. const wiredSetup = page() -wiredSetup.runtime = { connection: "wireless", running: false } -assert.equal(wiredSetup.wiredDisabled, false) -for (const field of ["enabled", "parked"]) { - const blocked = page({ ...setup, [field]: false }) - assert.equal(blocked.wiredDisabled, true, field) -} -wiredSetup.pairing = { active: true } -assert.equal(wiredSetup.wiredDisabled, true, "pairing owns the connection") -wiredSetup.pairing = null -wiredSetup.runtime.running = true -assert.equal(wiredSetup.wiredDisabled, true, "a running session cannot change connection") -wiredSetup.runtime = { connection: "wireless", running: false, wired_blocked: "USB is busy" } -assert.equal(wiredSetup.wiredDisabled, true, "another USB owner blocks enabling wired mode") -wiredSetup.runtime.connection = "wired" -assert.equal(wiredSetup.wiredDisabled, false, "the existing wired connection can still be disabled") - -// Jetlink only delays wired: it can be chosen, with a shortcut to switch Jetlink off. -wiredSetup.runtime = { connection: "wireless", running: false, wired_busy: "Turn off Jetlink to use wired Android Auto" } -assert.equal(wiredSetup.wiredDisabled, false, "Jetlink does not stop wired being chosen") -assert.equal(wiredSetup.wiredJetlinkBusy, false, "no notice until wired is chosen") -wiredSetup.runtime.connection = "wired" +wiredSetup.runtime = { connection: "wired", running: false, wired_busy: "Turn off Jetlink to use wired Android Auto" } assert.equal(wiredSetup.wiredJetlinkBusy, true) +assert.equal(wiredSetup.wiredStatus.tone, "expired") +assert.equal(wiredSetup.wiredStatus.label, "Wired Android Auto not enabled") assert.match(wiredSetup.connectReason, /Jetlink/) -assert.match(AndroidAutoPage.template, /href="#\/manage_models"/) +assert.match(AndroidAutoPage.template, /class="gx-link" href="#\/manage_models"/) +assert.match(AndroidAutoPage.template, /bi-arrow-right/) +wiredSetup.runtime.wired_busy = "" +assert.equal(wiredSetup.wiredStatus.tone, "ready") +assert.equal(wiredSetup.wiredStatus.label, "Wired Android Auto enabled") +assert.equal(wiredSetup.connectReason, "") +wiredSetup.runtime.wired_blocked = "USB is unavailable" +assert.equal(wiredSetup.wiredStatus.tone, "expired") +assert.equal(wiredSetup.connectReason, "USB is unavailable") +wiredSetup.runtime.wired_blocked = "" +wiredSetup.setup.enabled = false +assert.equal(wiredSetup.wiredStatus.tone, "expired") const uploadPage = page({ ...setup, enabled: false }) const uploadCalls = [] @@ -526,6 +519,7 @@ assert.deepEqual(namedCalls, [['car_type', { carType: 'adapter' }]]) const dongle = page(setup) dongle.runtime = { car_type: 'adapter', car_profile: 'default', car_profile_source: 'standard' } assert.equal(dongle.adapterRow.value, 'Choose one', 'no default: the owner picks') +assert.equal(settingControl(dongle.adapterRow), 'select', 'the initial adapter prompt must open a picker') assert.match(dongle.pairingReason, /adapter model/) dongle.runtime = { car_type: 'adapter', car_profile: 'default', car_profile_source: 'override' } assert.equal(dongle.adapterRow.value, 'Other') diff --git a/openpilot/starpilot/galaxy/web/js/android-auto.js b/openpilot/starpilot/galaxy/web/js/android-auto.js index ede2b08d2e..d7e123d154 100644 --- a/openpilot/starpilot/galaxy/web/js/android-auto.js +++ b/openpilot/starpilot/galaxy/web/js/android-auto.js @@ -47,8 +47,11 @@ const ADDRESS = /^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$/ const ADAPTER_MODELS = { aawireless: "AAWireless v1", default: "Other" } const HEAD_UNIT_APPS = { headunitpad: "HeadunitPad", openheadunit: "Open Headunit" } const HOTSPOT_LINK = `Galaxy Wi-Fi Hotspot` -const WIRED_JETLINK = `

Jetlink is using the USB-C port, so wired Android Auto can’t connect yet. Turn off Jetlink, then reconnect. - Open Jetlink settings

` +const WIRED_STATUS = `
+

{{ wiredStatus.label }}

+

{{ wiredStatus.detail }}

+ Turn off JetLink +
` const APP_SETUP = `
HeadunitPad setup (iPad) @@ -494,9 +497,11 @@ export const AndroidAutoPage = { app() { return HEAD_UNIT_APPS[this.runtime?.connection] || "" }, connectionName() { return this.app === "HeadunitPad" ? "iPad" : this.app ? "tablet" : this.wired ? "car" : this.selected?.name || "car" }, setupDone() { return !!(this.supportReady && (this.selected || this.wired || this.app) && !this.pairing?.active) }, - wiredRow() { - const detail = "Turn this on for wired Android Auto. Connect the car’s Android Auto USB data port to the comma’s USB-C port with a data-capable cable." - return switchRow("Wired AA", this.wired, detail + (this.runtime?.wired_blocked ? ` ${this.runtime.wired_blocked}.` : "")) + wiredStatus() { + if (this.runtime?.wired_blocked) return { tone: "expired", label: "Wired Android Auto not enabled", detail: this.runtime.wired_blocked } + if (this.wiredJetlinkBusy) return { tone: "expired", label: "Wired Android Auto not enabled", detail: "JetLink is using the USB-C port. Turn off JetLink, then return here." } + if (!this.setup?.enabled) return { tone: "expired", label: "Wired Android Auto not enabled", detail: "Turn on Android Auto Support in step 1." } + return { tone: "ready", label: "Wired Android Auto enabled", detail: "Connect the car’s Android Auto USB data port to the comma’s USB-C port with a data-capable cable." } }, appRow() { return { label: "App", value: this.app || "Off", choices: ["Off", ...Object.values(HEAD_UNIT_APPS)], available: true, action: true, @@ -516,17 +521,13 @@ export const AndroidAutoPage = { adapterRow() { const chosen = this.runtime?.car_profile_source === "override" return { label: "Adapter", value: !chosen ? "Choose one" : this.runtime.car_profile === "aawireless" ? ADAPTER_MODELS.aawireless : ADAPTER_MODELS.default, - choices: Object.values(ADAPTER_MODELS), available: true, action: true, + choices: [...(!chosen ? ["Choose one"] : []), ...Object.values(ADAPTER_MODELS)], available: true, action: true, reason: "AAWireless v1 gets video and car-data settings tuned for that dongle. Other adapters use the regular pairing." } }, appDisabled() { return !this.setup?.enabled || !this.setup.parked || !!this.pairing?.active || !!this.runtime?.running }, wiredJetlinkBusy() { return this.wired && !!this.runtime?.wired_busy }, - wiredDisabled() { - return !this.setup?.enabled || !this.setup.parked || !!this.pairing?.active || !!this.runtime?.running || - (!!this.runtime?.wired_blocked && !this.wired) - }, download() { return downloadLink(this.setup?.download, this.setup?.downloadPending === true) }, uploadPercent() { const { loaded, total } = this.uploadProgress || {} @@ -593,6 +594,7 @@ export const AndroidAutoPage = { if (!this.setup.serviceReady) return "Waiting for the Android Auto service to start." if (this.pairing?.active) return "Finish or cancel pairing first." if (this.adapterSetup && this.runtime?.car_profile_source !== "override") return "Choose your adapter model in step 2 first." + if (this.wired && this.runtime?.wired_blocked) return this.runtime.wired_blocked if (this.wiredJetlinkBusy) return "Turn off Jetlink first." return "" }, @@ -741,7 +743,7 @@ export const AndroidAutoPage = {

{{ projecting ? 'Connected to ' + connectionName : runtime?.running ? 'Connecting to ' + connectionName : connectionName }}

-

{{ runtime?.running && runtime.state !== 'streaming' && runtime.label ? runtime.label : runtime?.running ? 'Showing StarPilot on your car’s screen.' : runtime?.auto_connect ? 'Connects automatically when you start driving.' : 'Ready to connect.' }}

+

{{ runtime?.running && runtime.state !== 'streaming' && runtime.label ? runtime.label : runtime?.running ? 'Showing StarPilot on your car’s screen.' : wired ? wiredStatus.label : runtime?.auto_connect ? 'Connects automatically when you start driving.' : 'Ready to connect.' }}

{{ expiry.label }}

@@ -750,9 +752,7 @@ export const AndroidAutoPage = { - - ${WIRED_JETLINK} + ${WIRED_STATUS} ${APP_SETUP}
@@ -814,9 +814,7 @@ export const AndroidAutoPage = {