Initial minimal Connect IQ scaffold

Verifies the toolchain: builds and runs on fenix7 simulator and
Forerunner 265 hardware. No features yet — empty view rendering
"Einsatzprotokoll" on black background.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
EiSiMo 2026-04-11 19:26:43 +02:00
commit 6f56c337f7
9 changed files with 266 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
bin/
*.prg
*.prg.debug.xml
*.iq
.DS_Store

18
manifest.xml Normal file
View file

@ -0,0 +1,18 @@
<iq:manifest xmlns:iq="http://www.garmin.com/xml/connectiq" version="3">
<iq:application entry="EinsatzprotokollApp"
id="1a4e06bd8e5b484f96c291bde9937b3a"
launcherIcon="@Drawables.LauncherIcon"
minApiLevel="3.2.0"
name="@Strings.AppName"
type="watch-app">
<iq:products>
<iq:product id="fenix7"/>
<iq:product id="fr265"/>
</iq:products>
<iq:permissions/>
<iq:languages>
<iq:language>deu</iq:language>
</iq:languages>
<iq:barrels/>
</iq:application>
</iq:manifest>

1
monkey.jungle Normal file
View file

@ -0,0 +1 @@
project.manifest = manifest.xml

192
plan.md Normal file
View file

@ -0,0 +1,192 @@
# App: "Einsatzprotokoll"
Eine App für Polizisten, die es ermöglicht die Uhrzeit und den Standort von Ereignissen bequem mit ihrer Smartwatch zu protokollieren.
## Problem und Nutzungsszenario
Als Polizist muss man häufig Uhrzeiten und Standorte von Ereignissen für den späteren Bericht protokollieren. Beispielsweise das Eintreffen am Einsatzort, das Erfolgen einer Festnahme oder die Wegstrecke bei Verfolgungsfahrten.
Dafür muss man stehts den Notizblock zücken und ggfs. noch die aktuelle Adresse ermitteln. Das kostet Zeit und nerven in einem Stressigen Einsatz.
Da viele Polizisten ohnehin eine Garmin Smartwatch haben, ermöglicht diese App das einfache Protokollieren von Zeiten und Standorten ohne das umständliche notieren oder im nachinein Rekonstruieren.
## Zielgruppe
Die Zielgruppe sind zunächst nur Deutsche Polizisten, da ich selbst einer bin und die Anforderungen hier am besten einschätzen kann.
Später könnte die App international oder auch auf andere Einsatzkräfte (z.B Feuerwehr) ausgeweitet werden
## Anforderungen
1. Ein Eintrag soll immer das aktuelle Datum, Die Uhrzeit, Die Koordinaten und die nächstgelegene Adresse beinhalten.
2. Ein Eintrag soll extrem einfach und schnell über wenige Klicks an der Uhr zu erstellen sein.
3. Die App soll vorgefertigte Arten von Einträgen beinhalten um sauber trennen zu können, was wozu gehört. (Zum Beispiel: Festnahme, Einsatzbeginn, Einsatzende)
4. Die Historie soll einfach über die Uhr zugänglich sein. Alte Events werden nach 7 Tagen automatisch gelöscht, sodass der Speicher der Uhr niemals voll wird.
5. Bedienung soll rein über die Knöpfe möglich sein. Nicht über Touchscreen, damit man sie auch mit Handschuhen bedienen kann
## Design
# Farben
Der Hintergrund der gesamten App ist schwarz um auf AMOLED-Displays Akku zu sparen: 000000
Die Schrift ist Weiß: FFFFFF
Die Icons haben knallige, bunte, unterschiedliche Farben, damit sie schnell Identifizierbar sind.
### Öffnen der App
Die App wird über einen Glance geöffnet. Der Nutzer kann das Widget so personalisiert in der Liste seiner Glances hinzufügen und verschieben.
Die Glances erreicht man vom Normalen Watch Face über den "DOWN" Button.
### Glance
Das Glance enthält das App-Logo auf der linken Seite und als Überschrift den App Namen "Einsatzprotokoll"
Darunter wird der letzte Eintrag angezeigt:
Beispiel: "Festnahme: 11.04.2026 12:34"
Beim Klick auf das Glance öffnet sich die eigentliche Menü der App
Darüber Hinaus kann das App Menü auch auf Shortcuts gelegt werden, um sie noch schneller zu öffnen. Zum Beispiel auf "lange DOWN".
### Menü
Das Menü der App ähnelt vom Design und der Bedienung dem "Controls Menu", also dem Menü das man über das lange Drücken auf den "LIGHT" Knopf erreicht und Systemeinstellungen, wie Handy Orten, Notfall oder Ausschalten beinhaltet.
Es soll also ebenso ein Kreis mit 10 Icons sein, welche sich über die "UP" und "DOWN" Knöpfe rotieren lassen. Das Icon auf Höhe des "START/STOP" Knopfes soll durch einen Dezenten visuellen Hinweis hervorgehoben werden. Mit einem Klick auf "START/STOP" wird es dann ausgewählt.
Das Menü merkt sich, den zuletzt ausgewählten Eintrag und ist beim nächsten öffnen wieder so rotiert, wie beim letzten verlassen.
In der Mitte des Auswahlringes befinden sich Informationen zum aktuell ausgewählten Element als einfacher, zentrierter Text.
Die Menüpunkte sind folgende:
| Nummer | Name | Icon |
| ------ | ----------------------- | ------------------------ |
| 1 | Verlauf | Liste mit Bulletpoints |
| 2 | Allgemeines Ereignis | Plus-Symbol |
| 3 | Einsatzbeginn | Play-Symbol |
| 4 | Einsatzende | Häkchen im Kreis |
| 5 | Eintreffen | Pin mit Pfeil nach unten |
| 6 | Festnahme | Handschellen |
| 7 | Zwanganwendung | Geballte Faust |
| 8 | Beweismittel gesichert | Kamera |
| 9 | Sichtung | Fernglas |
| 10 | Letzten Eintrag löschen | Mülleimer |
Die Icons sollten knallige Farben haben.
### Verlauf
Der Verlaufsbildschirm ist in 3 Horizontal geteilte Bereiche Unterteilt.
- Oberer Bereich: 15% der gesamten Bildschirmhöhe
- Zeigt nur das Icon des chronologisch vorherigen Eintrages mit einem "Pfeil hoch" Icon.
- Mittlerer Bereich: 70% der gesamten Bildschirmhöhe
- In den mittleren 70% sind folgende Informationen:
- Eintragsart
- Datum + Uhrzeit
- Koordinaten oder die Adresse (Adresse nur wenn sie nicht weiter als 20 Meter von der den Koordinaten entfernt ist)
- Unterer Bereich: 15% der gesamten Bildschirmhöhe
- Zeigt nur das Icon des chronologisch nächsten Eintrages mit einem "Pfeil runter" Icon.
Erst wenn der User den Eintrag hier geöffnet hat wird die Adresse zu den Koordinaten ermittelt. So wird während der Erstellung die Fehleranfälligkeit minimiert.
Falls kein Handy verbunden ist soll statt der Adresse "Verbinde Handy für Adresse" angezeigt werden.
### Erstellung
Nachdem im Menü ein Ereignis mit dem Druck auf "START/STOP" ausgewählt wurde, wird zunächst ein Ladescreen angezeigt während die GPS Koordinaten bestimmt werden. Dieser ist wie folgt aufgebaut.
Am runden Rand der Uhr zirkuliert ein weißer Balken der symbolisiert, dass der Ladevorgang läuft. In der Mitte steht "Standort wird bestimmt".
Wenn der Vorgang abgeschlossen ist, verschwindet der Ladebalken und es wird ein grüner Haken für 2.5 Sekunden angezeigt. Außerdem vibriert die Uhr kurz. Dannach schließt sich die App und der User ist wieder auf dem normalen Watchface.
Sollte der Vorgang aus irgendeinem Grund nicht erfolgreich sein, wird statt dem Haken für 5 Sekunden eine Fehlermeldung angezeigt. Diese sollte auf Deutsch und möglichst wenig technisch sein. Außerdem vibriert die Uhr drei Mal.
Die GPS Accuracy sollte möglichst auf mindestens 5m genau sein. Nach 10 Sekunden Ladezeit sollte jedoch einfach das aktuell beste Ergebnis gespeichert werden. Wenn kein GPS Signal gefunden werden konnte, werden die Koordinaten auf None gesetzt.
Die Uhr Speichert für jedes Ereignis folgende Werte:
- Ereignisname (String)
- Timestamp
- Koordinaten
- GPS Accuracy
### Löschen
Wenn der Nutzer im Menü "Löschen" auswählt beginnt ein roter, runder Balken auf höhe des "START/STOP" Knopfes um die Uhr zu laufen, während die Taste gedrückt gehalten wird. Der User muss die Taste 2.5 Sekunden gedrückt halten um das Löschen auszulösen. So wird verhindert, dass ausversehen Einträge gelöscht werden.
## Technische Anforderungen
### Tech Stack
- Programmiersprache: Monkey C
- Framework/SDK: Garmin Connect IQ (CIQ) SDK
### Adressen Auflösung
Die Adressen werden über meine selbst gehostete Photon Instanz unter "gps.moritz.run" aufgelöst.
**Beispiel:**
```
GET https://gps.moritz.run/reverse?lon=13.517740504968456&lat=52.52408130696999
```
```json
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"osm_type": "N",
"osm_id": 11286301180,
"osm_key": "place",
"osm_value": "house",
"type": "house",
"housenumber": "59A",
"street": "Rhinstraße",
"district": "Lichtenberg",
"city": "Berlin",
"country": "Deutschland",
"postcode": "10315",
"countrycode": "DE"
},
"geometry": {
"type": "Point",
"coordinates": [
13.517589,
52.5239594
]
}
}
]
}
```
Die Implementierung der eigenen Instanz steht noch aus. Zum testen und für die Entwicklung sollte dieser öffentliche Endpunkt verwendet werden.
```
https://photon.komoot.io/reverse?lon=13.38&lat=52.51
```
### Logging
Zur Verfolgbarkeit von Abstürzen speichert die Uhr Fehlermeldungen nach abstürzen. Auch diese werden jedoch nur 7 Tage vorgehalten.
### Coding Anforderungen
1. App auf Deutsch. Code auf Englisch.
2. Nur Kommentare an kritischen Stellen.
3. Moderne, objektorientierte Programmierung
4. Saubere Github Commits für Versionshistorie
5. Einstellungen, Schwellwerte, Zeiten für Animationen, Strings usw. sollten in zentralen Dateien gespeichert, und entsprechend kommentiert werden, sodass sie leicht angepasst werden können.
### Hardware
Die App soll auf allen modernen Garmin Modellen Funktionieren. Das Layout muss also relativ zur Bildschirmgröße berechnet werden.
### Icon-Quellen
Die Icons werde ich selbst erstellen. Für die Entwicklung genügen Platzhalter Icons.
### Probleme
1. Nächstgelegene Adresse von Koordinaten ermitteln.
- Geht nur über eine HTTP API die über die Bluetooth Verbindung zum Handy abgerufen werden muss. Hier steht eine finale Entscheidung aus.
2. Datenschutz und Dienstrecht
- Um Rechtliche Probleme zu minimieren syncronisiert die App keine Nutzerdaten mit der Garmin Cloud. Alle Einträge bleiben nur Lokal auf der Uhr. Lediglich die Koordinaten müssen das Gerät zur Adressermittlung verlassen.
- Außerdem wird ein Dienstrechtlicher Disclaimer in die App-Beschreibung eingebaut.
## Ideen und Next steps:
Sobald der Prototyp steht wird näher Über diese Features nachgedacht.
1. Asynchrones Ermitteln der GPS-Koordinaten im Hintergrund.
- Pro
- Der Nutzer kann die Uhr gleich weiter nutzen. Er muss nicht auf ein GPS-Signal warten.
- Contra
- Der Nutzer bekommt kein Feedback ob die Signalfindung erfolgreich war
- Es ist schwerer zu implementieren und fehleranfälliger
2. Im Menü ein weiterer Punkt "Einstellungen"
- Hier könnte der Nutzer die Menüanordnung personalisieren
- Außerdem könnte er eigene Ereignisarten erstellen
- Er könnte Zeiten und variablen verändern
- Pro
- Mehr Personalisierungsmöglichkeiten
- Breitere Anwendungsbereiche
- Contra
- Mehr Komplexität für den Nutzer: Er könnte überfordert sein
- Schwierige Implementierung auf einem kleinen Smartwatchdisplay
- Großer Aufwand bei Programmierung, Wartung und Design
3. Neuer "Verfolgungsmodus"
- Bei Verfolgungsjagden muss man später die genaue Wegstrecke rekonstruieren. In diesem Modus zeichnet die Uhr konstant die Wegstrecke auf. Außerdem wird auf der Uhr die aktuellen Straße auf der man Fährt angezeigt und die Fahrtrichtung um dies über Funk durchzugeben.

View file

@ -0,0 +1,3 @@
<drawables>
<bitmap id="LauncherIcon" filename="launcher_icon.svg"/>
</drawables>

View file

@ -0,0 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" fill="#000000"/>
<path d="M3 4C3 3.44772 3.44772 3 4 3H10C10.5523 3 11 3.44772 11 4V10C11 10.5523 10.5523 11 10 11H4C3.44772 11 3 10.5523 3 10V4Z" fill="#F4F4F4"/>
<path d="M3 14C3 13.4477 3.44772 13 4 13H10C10.5523 13 11 13.4477 11 14V20C11 20.5523 10.5523 21 10 21H4C3.44772 21 3 20.5523 3 20V14Z" fill="#F4F4F4"/>
<path d="M13 4C13 3.44772 13.4477 3 14 3H20C20.5523 3 21 3.44772 21 4V10C21 10.5523 20.5523 11 20 11H14C13.4477 11 13 10.5523 13 10V4Z" fill="#F4F4F4"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.3 14.3V19.7H19.7V14.3H14.3ZM14 13C13.4477 13 13 13.4477 13 14V20C13 20.5523 13.4477 21 14 21H20C20.5523 21 21 20.5523 21 20V14C21 13.4477 20.5523 13 20 13H14Z" fill="#F4F4F4"/>
</svg>

After

Width:  |  Height:  |  Size: 832 B

View file

@ -0,0 +1,3 @@
<strings>
<string id="AppName">Einsatzprotokoll</string>
</strings>

View file

@ -0,0 +1,18 @@
import Toybox.Application;
import Toybox.Lang;
import Toybox.WatchUi;
class EinsatzprotokollApp extends Application.AppBase {
function initialize() {
AppBase.initialize();
}
function onStart(state as Dictionary?) as Void {}
function onStop(state as Dictionary?) as Void {}
function getInitialView() as [WatchUi.Views] or [WatchUi.Views, WatchUi.InputDelegates] {
return [ new EinsatzprotokollView() ];
}
}

View file

@ -0,0 +1,19 @@
import Toybox.Graphics;
import Toybox.Lang;
import Toybox.WatchUi;
class EinsatzprotokollView extends WatchUi.View {
function initialize() {
View.initialize();
}
function onUpdate(dc as Dc) as Void {
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLACK);
dc.clear();
var cx = dc.getWidth() / 2;
var cy = dc.getHeight() / 2;
dc.drawText(cx, cy, Graphics.FONT_MEDIUM, "Einsatzprotokoll",
Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER);
}
}