De kaart van Google, mijn flitsers, één app
Ik wilde één ding op het autoscherm. De kaart van Google, het verkeer van Google en de turn-by-turn van Google, met mijn eigen flitsergegevens erbovenop, en een melding alleen voor flitsers die echt op mijn kant van de weg gericht staan.
Ik kon op Android Auto niets vinden dat dat deed. Google Maps heeft de kaart en de routes, maar je kunt er op geen enkele manier je eigen flitserdatabase in laden. Dus bouw ik het zelf.
Het heet AutoNavAlerts. Het is een navigatie-app voor Android Auto, gebouwd op de Navigation SDK van Google, met daarbovenop een meldsysteem voor flitsers en andere interessante punten. De eerste bron voor flitsers is PocketGPSWorld, met mijn eigen betaalde abonnement. De code is openbaar onder de Apache License 2.01 op github.com/damo2929/AutoNavAlerts.
Dit is deel één van een serie. Het gaat over wat de app doet, wat Google een app van derden op een autoscherm wel en niet laat doen, en hoe hij gebouwd is, want bijna elke regel ervan is geschreven door Claude, het AI-model van Anthropic, terwijl ik de regels stelde, testte en eigenaar ben van het resultaat. Dat laatste is een verhaal op zich.
Het is versie 0.1.0. Hij is gesideloaded, staat niet in de Play Store en heeft één echte rit gehad. Lees het als een bouwlogboek, niet als een productlancering.
Wat het vandaag doet
Dit zit er in de testbuild, en hier is waar elk onderdeel voor is:
| Onderdeel | Wat het doet |
|---|---|
| Navigatie | Kaart van Google met verkeer, plaatsen zoeken, routevoorbeeld met de aankomsttijd, turn-by-turn op het autoscherm, de zijbalk en het bestuurdersdisplay |
| Naar een contact rijden | Zeg of typ een naam en de opgeslagen adressen van het contact verschijnen boven de resultaten van Places. Alleen het adres dat je kiest gaat naar Google |
| Samen met je muziek | Alleen navigatiesjablonen, zodat de muziek haar kaart houdt in gesplitst scherm. Gesproken meldingen zetten de muziek zachter, nooit op pauze |
| Flitsermeldingen | Drie fasen per flitser, alleen voor flitsers die naar je rijrichting kijken, op de weg waar je rijdt |
| Trajectcontroles | Een traject begint alleen als er een volgende flitser met dezelfde limiet voor je ligt, en eindigt bij de laatste flitser |
| Snelheidspaneel | Je snelheid en de limiet van de weg, altijd naast elkaar, rechtsonder |
| Snelheidslimieten offline | Eén download van OpenStreetMap-limieten voor het VK: 1.326.619 wegen in 29 MB op de telefoon. Tijdens het rijden wordt niets opgezocht |
| Routes voor elektrische auto’s | De snelste route en de energiezuinige route van Google naast elkaar, en jij kiest |
| Flitsergegevens | Je eigen PocketGPSWorld-abonnement, dagelijks gecontroleerd, nooit gewisseld terwijl je navigeert. Of importeer iGO-, KML-, GPX-, OV2- of CSV-bestanden |
| Een flitser melden | Eén tik in de auto bewaart positie, richting en limiet, met optioneel gesproken commentaar. Nakijken en versturen gebeurt op de telefoon, nooit tijdens het rijden |
De flitsermeldingen zijn de reden dat de app bestaat, dus die krijgen het volgende hoofdstuk.
Een flitser telt alleen als hij naar jou kijkt
Een flitsermelding die afgaat voor de andere rijbaan is erger dan geen melding. Ze leert je het ding te negeren. Dus de eerste harde regel in het ontwerp is dat een flitser alleen meldt als hij naar je rijrichting kijkt, en die regel wordt nooit versoepeld, nooit met een standaardwaarde omzeild en nooit losgelaten als de gegevens lastig zijn.
De engine draait bij elke positiebepaling, tot tien per seconde. Voor elke flitser in de buurt stelt hij vier vragen, op volgorde:
| Controle | Geslaagd als | Waarom die er is |
|---|---|---|
| Dichtbij genoeg | binnen de meldafstand plus 200 m, met een maximum van 3 km | geen werk aan flitsers die kilometers ver weg staan |
| Voor je | koers naar de flitser binnen 45° van je rijrichting | een flitser achter je is geschiedenis |
| Kijkt jouw kant op | de richting van de flitser binnen 45° van je rijrichting, of van de koers naar de flitser in een bocht | de andere rijbaan blijft stil |
| Op jouw weg | binnen 50 m van de route voor je, of een corridor van 40 m die met de afstand breder wordt bij vrij rijden | de parallelle weg blijft stil |
Die 45° is niet ruim. De TomTom-bestanden van PocketGPSWorld geven de richting van een flitser in acht windrichtingen, dus 22,5° ervan gaat al op aan de gegevens voordat er iets anders gebeurt. Een GPS-koers kost nog een paar graden, en de kromming van de weg nog een paar. Daarom mag niets anders er iets van afsnoepen.
Een flitser die de richtingcontrole niet haalt, wordt nog steeds op de kaart getekend, gedimd, met een pijl die laat zien welke kant hij op kijkt. Je kunt hem zien. Hij praat alleen niet tegen je.
De fasen zijn getimed, geen vaste afstanden. De eerste melding komt 15 seconden van tevoren bij je huidige snelheid en de tweede op de helft daarvan, allebei met een ondergrens zodat ze de derde nooit verdringen. De derde ligt vast op 100 yards, en die is verplicht zolang het geluid aan staat. De meldafstand kan tijdens een nadering alleen groeien, dus hard remmen maakt de waarschuwing waar je al recht op had niet korter.
Wat Google een app op het autoscherm laat doen
Hier wordt het interessant, want een app van derden op Android Auto is geen Google Maps. Hij draait binnen de regels van de Car App Library van Android Auto, die apps “a set of visual templates approved for driver safety”2 geeft, en binnen wat de Navigation SDK besluit open te stellen.
Drie gaten hebben de app meer gevormd dan alles wat ik zelf koos te bouwen:
| Wat | Google Maps in de auto | Een app op de Navigation SDK |
|---|---|---|
| Een ongeluk, politie of wegwerkzaamheden melden | ingebouwd | niet aangeboden. De eigen documentatie van Google: “real-time disruptions are not available on Android Auto”3 |
| De snelheidslimiet van de weg | getoond | alleen het percentage dat je erboven zit, en alleen zolang je te hard rijdt4 |
| Aanraken van de eigen kaartknoppen van de SDK | n.v.t. | niets. Android Auto “limits screen surface interaction to a series of SurfaceCallback methods”2 |
Neem de snelheidslimiet. Google weet hem. Google vertelt een app hoeveel je erboven zit, als percentage en een ernst, en helemaal niets zolang je eronder zit. Mag ik vragen waarom een bestuurder de limiet pas mag weten als hij hem al overtreedt? Daar ga ik geen antwoord op krijgen, dus de app werkt eromheen. Zolang je te hard rijdt, rekent hij de limiet terug uit je snelheid en het percentage. Daaronder gebruikt hij de flitser die je nadert, daarna de OpenStreetMap-download, en hij toont een streepje in plaats van niets als hij helemaal geen limiet heeft.
Incidenten melden heeft dezelfde vorm. De meld-API’s bestaan in de navigatieweergave op de telefoon en niet in die van de auto. Iemand heeft Google er al om gevraagd5, en dat verzoek staat nog open. De app controleert tijdens het draaien of de API’s er zijn, dus als een latere SDK ze toevoegt, verschijnt de meldknop van Google zonder enige wijziging in mijn code. Een GitHub Actions-job controleert het elk kwartaal en opent een issue op de dag dat het gebeurt.
De aanraakregel had me goed te pakken. De autokaart van de SDK tekent zijn eigen aankomstkaart, kop, centreerknop, snelheidsmeter en ritbalk. Geen ervan reageert op een tik, want geen aanraking bereikt de weergave van de SDK. Ze zien eruit als knoppen en doen helemaal niks. Dus staan ze allemaal uit, en de app tekent zijn eigen knoppen op de plek waar een tik echt aankomt.
Breng je eigen sleutel mee, breng je eigen gegevens mee
Twee beslissingen houden deze app eerlijk over geld en licenties, en allebei zijn ze bewust.
Er zit geen ingebouwde Maps-sleutel in. Google Maps Platform vereist “a billing-enabled Cloud project”6, en de Navigation SDK een project waarin hij is ingeschakeld7. Als ik een sleutel meeleverde, zou ieders rijden op mijn rekening komen, of de sleutel zou uitlekken en alsnog op mijn rekening komen. Dus brengt elke gebruiker zijn eigen sleutel mee, in zijn eigen Google Cloud-account. De telefoonapp kan er een aanmaken met je eigen Google-account, of je plakt er een in. Hij wordt versleuteld op de telefoon bewaard. Er is geen enkele terugvaloptie, en de app doet geen enkele SDK-aanroep voordat jouw sleutel erin zit.
Er zitten geen flitsergegevens in de app. PocketGPSWorld geeft zijn database in licentie voor individueel gebruik, voorziet elke download van een watermerk voor het lid, en verbiedt hem ergens neer te zetten waar iemand anders dan de koper erbij kan. Dus de app levert hem nooit mee, host hem nooit en synchroniseert hem nooit tussen telefoons. Je logt in met je eigen abonnement, en de telefoon downloadt je eigen kopie met watermerk naar privéopslag die buiten de back-ups valt. Op de testopstelling waren dat 19.826 flitsers in 33 sets, één set per bestand, nooit samengevoegd.
De automatische synchronisatie is daar beleefd in. Hij leest één keer per dag de openbare RSS-feed van PocketGPSWorld8 en logt alleen in als er een nieuwe uitgave is. Hij wisselt de flitsers nooit terwijl je navigeert.
Eén clausule in hun licentie is hiermee niet helemaal geregeld, die over het opnemen van de gegevens in een applicatie. Voor mijn eigen gebruik accepteer ik dat. Voordat de app naar iemand anders gaat, krijgt PocketGPSWorld een e-mail met de vraag.
Twee dagen, 87 commits, en eerst een ontwerpdocument
Nu het deel waar mensen over gaan discussiëren. Ik heb deze code niet getypt. Claude wel, via Claude Code, met twee modellen, Claude Fable en Claude Opus. Ik schreef de eisen, nam elke beslissing, draaide de testopstelling, reed de auto en beoordeelde wat terugkwam. Elke commit met door AI geschreven wijzigingen erin draagt een trailer die dat zegt, en de README ook.
Dit zijn de cijfers, afgelezen uit de repository:
| Meting | Waarde |
|---|---|
| Commits | 87, van 22:10 op 11 oktober tot 12:44 op 13 oktober 2026 |
| Commits met een Claude co-author-regel | 83 (56 Fable, 27 Opus) |
| Kotlin in de app en zijn kernbibliotheek | 15.334 regels |
| Unittests | 211, in 3.464 regels |
DESIGN.md, de specificatie | 1.763 regels, geschreven voordat er code was |
Het ontwerpdocument kwam eerst, en dat is het nuttigste dat ik iemand kan vertellen die dit doet. De openingsregel zegt dat er geen code wordt geschreven voordat het is goedgekeurd, en dat elke latere wijziging daar eerst wordt gemaakt. Elke klasse heeft een taak die ernaast staat opgeschreven. Elke drempel heeft een reden. Elke regel die ik stelde, staat als de mijne gemarkeerd, met de datum.
Dat doet ertoe omdat een model vergeet. Een lange sessie wordt samengevat, en de samenvatting houdt de grote lijn en laat de details vallen, en in de details zitten de bugs. De eigen AGENTS.md van de repository zegt het ronduit: vertrouw niet op een samenvatting van deze bestanden uit je geheugen of uit een eerdere sessie. De agent gaat terug naar het document. Elke keer.
In de tweede fase werkten ook twee agents tegelijk, de een aan begeleiding, de kaart en schermen, de ander aan locatie, meldingen, spraak en kaartmarkeringen. Elk had een opgeschreven lijst bestanden, en de interfaces ertussen lagen vooraf vast: toevoegen, niet hernoemen. Ze voegden schoon samen omdat de grens besloten was voordat een van beide begon, niet achteraf ontdekt.
Elke harde regel kwam van iets wat ik zag
Het ontwerp heeft een lijst harde regels, en niet één ervan kwam van een whiteboard. Elke regel kwam van kijken hoe het ding draaide op de Desktop Head Unit9, de emulator van Google voor het autoscherm op de pc, of in de auto, en zien dat het iets verkeerd deed.
| Harde regel | Wat ik zag waardoor het een regel werd |
|---|---|
| Een flitser meldt alleen als hij jouw kant op kijkt | meldingen voor de andere rijbaan zijn ruis die je leert meldingen te negeren |
| Niets op het autoscherm dat niet werkt | twee stopknoppen op het scherm, waarvan één dood |
| Geen functie twee keer getoond | datzelfde paar, plus twee centreerknoppen, twee snelheidsweergaven |
| Eén annuleerknop, die van Google | de kaart in de hoek met de X is van Android Auto zelf, getekend uit de rit die de app publiceert, dus de eigen stopknop van de app verdween |
| Geen getril op het autoscherm | het scherm Meer trilde terwijl de auto geparkeerd stond, omdat het elke seconde een identiek scherm opnieuw stuurde en Android Auto elke verversing animeert |
| Elke knop aan de rechterkant | de kaartknoppen stonden linksboven tot het autoscherm zijn afmetingen doorgaf; nu vanaf het begin één kolom langs de rechterrand, niets van ons links |
| Geen ingebouwde Maps-sleutel, nooit | niemands rijden komt op de rekening van een ander |
Het getril is mijn favoriet, omdat het zo klein is en het mee zou zijn gegaan. Een live GPS-regel tikte één keer per seconde. Elke tik stuurde het hele scherm opnieuw, ongewijzigd, en de host animeerde het, zodat elke regel eronder wiebelde terwijl de auto stilstond. De oplossing is één vergelijking: alleen opnieuw tekenen als een regel veranderd is. De regel is groter dan de oplossing. Een scherm dat stilstaat moet er stil uitzien, en dat wordt gecontroleerd met een reeks schermafbeeldingen die identiek moeten zijn.
Dat is het echte werk als een AI de code schrijft. Hij wordt niet moe en hij vindt herschrijven niet erg, maar hij kan het autoscherm niet zien. Jij wel. Wat je zag opschrijven als een regel, met de datum, is wat voorkomt dat het drie sessies later terugkomt.
Wat de code fout deed, en hoe het werd gevonden
Genoeg. Niets ervan was exotisch, en elk ervan werd gevonden door het te draaien, niet door het te lezen:
| Fout | Wat het deed | Hoe het werd gevonden |
|---|---|---|
| Route aan het verkeerde deel gekoppeld | De route van Google vanaf een geparkeerde auto ging naar het noorden, keerde op een rotonde en kwam over dezelfde weg terug naar het zuiden. De matcher zette de auto vast op het noordelijke deel 10 m verderop, dus elke flitser richting zuiden lag meer dan een kilometer “naast de weg”. Er kwam geen enkele melding | een gescripte rit op de testopstelling; het meldlogboek gaf offRoad lateral=1204 voor elke flitser |
| Spraakwaakhond te kort | die gaf 1,5 s plus 70 ms per teken. Getallen uitspreken duurt langer dan dat, dus elke melding in de tweede fase werd midden in de zin afgekapt | de spraakregistraties van een testrun |
| Een snelheidsbegrenzing in de engine | een venster van drie seconden betekende dat van twee flitsers op één kruising er één voorgoed verdween | het nalopen van de meldingen die stil bleven tijdens de eerste echte rit |
| Een verouderde koers | de emulator meldt een koers van 0°, wat je ook doet, dus op een weg naar het zuiden haalde elke flitser de richtingcontrole niet | een gesimuleerde rit over de A61 |
| Limietwijzigingen uitgesproken als flitsermeldingen | een limietwijziging verderop werd in de wachtrij gezet als flitsermelding in de eerste fase | het spraaklogboek op de testopstelling |
De tweede is het waard om even bij stil te staan. Elke afzonderlijke test slaagde. De code was correct naar zijn eigen maatstaf. Hij had alleen een time-out, gekozen door iemand die nog nooit een telefoon een afstand hardop had horen voorlezen. De test die dat vangt is een echte stem, getimed, op een echte rit.
De eerste echte rit
Op 12 oktober zette ik een releasebuild op mijn telefoon en nam hem mee in de XPENG G6 via draadloos Android Auto. Twee dingen gingen mis. Een route die ik geparkeerd had ingesteld, wiste zichzelf binnen een paar seconden nadat ik wegreed. En sommige meldingen werden uitgesproken en andere niet, zonder patroon dat ik kon zien.
Toen het ergere probleem. Er viel niets na te lezen. ColorOS op mijn telefoon gooit de logregels van de app weg, dus de rit liet helemaal geen bewijs achter. Was het de app, de SDK van Google of Android Auto zelf? Ik kon het niet zeggen.
Dus de oplossing voor de eerste rit was geen codewijziging. Het was bewijs. De app schrijft nu de hele tijd twee logboeken op de telefoon, een voor routes en de kaart en een voor elke meldbeslissing, in batches van 15 seconden, plus een optionele ritrecorder die elke positie bewaart zodat een rit op de testopstelling opnieuw kan worden afgespeeld. Daarna werden de gissingen gerepareerd, elk zo geschreven dat hij minder doet en logt dat hij het deed. Elke manier waarop de begeleiding kan eindigen, legt nu vast wie haar beëindigde.
Op de testopstelling heeft Android Auto sindsdien één keer stop aangeroepen, 2 minuten 35 seconden in een begeleide rit. Ik zat op dat moment door de head unit te klikken, dus het was waarschijnlijk de X op zijn eigen kaart. Het is niet gebeurd zonder dat iemand het scherm aanraakte. De volgende rit beslist het, want deze keer laat de rit een spoor achter.
Wat er hierna komt
De lijst voor de testopstelling is nog lang. De volgende ritten controleren de annuleerknop, de scenario’s met een bocht en met de verkeerde richting, de overdracht tussen de GPS van de auto en die van de telefoon, en of het bestuurdersdisplay in de G6 überhaupt afslagkaarten van een app van derden toont.
Latere delen van deze serie gaan behoorlijk in op de wiskunde van de meldengine, de omwegen voor de snelheidslimiet, de formaten van PocketGPSWorld, en de testopstelling zelf: hoe je een Android Auto-app test zonder auto, met een gescripte GPS en een emulator die denkt dat elke weg naar het noorden loopt.
De machine typt, de standaard is van jou
Ik heb veel gepraat over AI die code schrijft voorbij zien komen, het meeste van mensen die het verkopen of mensen die er bang voor zijn. Wat ik vond is saaier dan allebei, en nuttiger.
Hij typt snel en hij verveelt zich niet. Hij herschrijft een scherm voor de vierde keer zonder te zuchten. Wat hij niet doet, is merken dat het scherm Meer trilt, of dat een zin werd afgekapt, of dat er een dode knop naast een levende zit. Hij kan de auto niet zien. Dat was aan mij om te zien, en de enige reden dat het gerepareerd is, is dat ik elk ervan heb opgeschreven als een regel die hij moet lezen voordat hij iets aanraakt.
Als zodanig verschuift de verantwoordelijkheid niet. De code is van mij omdat ik de standaard stel waaraan hij wordt gehouden en omdat ik degene in de auto ben. Niets van wat het eerlijk hield is een AI-methode. Eerst de specificatie schrijven en dan de code, opschrijven wat je leert, en bewijs bewaren in plaats van gissen: zo is degelijke techniek altijd gedaan. Het gereedschap verandert dat niet. Het maakt het alleen sneller om te zien wie het oversloeg.
Apache License, Version 2.0 — de licentie waaronder de code van AutoNavAlerts is gepubliceerd. ↩︎
Google — Navigation SDK for Android: Android Auto — de Car App Library biedt “a set of visual templates approved for driver safety”, en “Android Auto limits screen surface interaction to a series of SurfaceCallback methods”. ↩︎ ↩︎
Google — Navigation SDK for Android: real-time disruptions — “real-time disruptions are not available on Android Auto”. ↩︎
Google — Navigation SDK for Android: speed alerts —
onSpeedingUpdated(float percentageAboveLimit, SpeedAlertSeverity speedAlertSeverity): het percentage boven de limiet en een ernst, niet de limiet zelf. ↩︎googlemaps/flutter-navigation-sdk issue #726 — “Incident reporting on Android Auto & CarPlay”, geopend op 17 juni 2026, staat nog open. ↩︎
Google Maps Platform — Get started — “you must create a billing-enabled Cloud project and enable the desired APIs or SDKs”. ↩︎
Google — Navigation SDK for Android: set up your project — “A Google Cloud console project with the Navigation SDK enabled.” ↩︎
PocketGPSWorld — de Britse flitserdatabase waaruit de app synchroniseert, met het eigen abonnement van elke gebruiker. ↩︎
Android Developers — Test using the Desktop Head Unit — de emulator van Google voor het autoscherm van Android Auto. ↩︎