No description
  • JavaScript 53.3%
  • Kotlin 23.4%
  • HTML 15.7%
  • Python 4.2%
  • CSS 3.4%
Find a file
2026-10-25 13:03:44 +01:00
app Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
Archive Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
gradle Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
tests Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
web Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
.gitignore Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
build.gradle.kts Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
COPYRIGHT Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
gradle.properties Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
gradlew Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
gradlew.bat Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
icon-fdroid.png Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
LICENSE Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
make-icon.py Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
make-skizzen.py Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
README.md Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00
settings.gradle.kts Stand vom 2026-10-25 2026-10-25 13:03:44 +01:00

Motive

Merkliste für Orte, an die man mit der Großformatkamera zurückkommen will.

Das Problem: unterwegs sieht man ein Motiv, das sich für 4×5 lohnen würde — die Kamera steht aber zu Hause, und bis man wieder dort ist, hat man es vergessen. Die App hält deshalb genau das fest, was zum Wiederkommen nötig ist:

  • schnelle Telefonfotos als Gedächtnisstütze (das eigentliche Bild entsteht später auf Film) — beliebig viele je Motiv, das erste ist das Titelbild,

  • den Standort, sofort und ohne Nachfragen,

  • eine Notiz („300er mitnehmen“),

  • zwei Stufen — wie dringend und wie aufwendig —, nach denen sich sortieren lässt, dazu nach Nähe zum jetzigen Standort.

Was fotografiert ist, wird abgehakt. Auf der Karte liegen alle Motive als Nadeln mit ihrem Vorschaubild.

Wer an einer Serie arbeitet, legt sich Themen an („Lüftungsanlagen", „Architektur", „Brücken"). Ein Motiv kann zu mehreren gehören — eine Lüftungsanlage ist oft auch Architektur. Über die Leiste im Kopf lässt sich die Liste (und die Karte) auf eine Serie einengen; die Sortierung Nach Thema zeigt stattdessen alle Serien untereinander, jede mit eigener Überschrift.

Jedes Foto lässt sich im Vollbild ansehen (zwei Finger oder Doppeltipp zum Zoomen) und darin mit einem Rechteck der geplante Ausschnitt markieren — das Telefon fotografiert im Handyformat, die Großformatkamera sieht anders. Fotos kommen immer dazu, sie ersetzen nie eins; Löschen lässt sich zurücknehmen.

Jedes Foto trägt seine Aufnahmezeit (aus dem EXIF, in Ortszeit — nicht der Zeitpunkt der Übernahme) und eine eigene Notiz: ein Motiv in der Dämmerung hängt an der Uhrzeit, und ein Referenzbild von 18 Uhr und eines von 22 Uhr sind zwei verschiedene Auskünfte. Weil ein Ort mehrfach erkundet wird, hat jedes Motiv außerdem ein Logbuch — die Fotos stehen mit ihrer Uhrzeit von allein darin, Text kommt mit einem Griff dazu („Tor war zu, links über die Wiese“).

Bewusst nicht enthalten: Belichtungsrechner, Balgenauszug, Schwarzschild, Filterfaktoren, Sonnenstandstabellen. Das ist eine Merkliste, kein Rechenknecht.

Am großen Bildschirm: das Archiv

Seit 1.6.0 gibt die App ihren Bestand an das gemeinsame Archiv weiter (Repo thee/Archive, Bereich motive). Am Telefon ändert das nichts — die App tut, was sie tat. Dazu kommt nur ein Weg nach draußen, und am anderen Ende liegt, was auf einem Telefonschirm keinen Platz hat:

  • alle Fotoorte auf einer bildschirmgroßen Karte, mit der Blickrichtung als Strich an jedem Punkt,
  • wann sich ein Motiv lohnt: aus Punkt, Blickrichtung und Uhrzeit rechnet das Archiv aus, ob die Sonne im Rücken steht, von der Seite kommt oder ins Objektiv scheint. Auf der Karte als Schieber für alle Motive zugleich, auf dem Blatt eines Motivs als Tagesband.

Dass die Sonnenrechnung dort steht und nicht hier, ist dieselbe Entscheidung wie oben: eine Tabelle mehr auf dem Telefon hilft niemandem, eine große Karte am Schreibtisch schon.

Eingerichtet wird es unter Menü → ⚙ Archiv einrichten (Adresse und Token). „Von selbst übergeben" steht auf Immer: was im Funkloch entsteht, geht raus, sobald ein Netz da ist. Wer Datenvolumen zählt, stellt Nur im WLAN ein.

Technisch hängt dafür das Repo thee/Archive als Submodul unter Archive/. Benutzt wird daraus nur ArchivClient (die Übergabe) und automatik.js (wann sie losfährt) — Motive ist keine Schalen-App, sondern eine, die ans selbe Archiv liefert. Nach einem frischen Klon einmal:

git submodule update --init

Aufbau

web/            die ganze App (app.html + app.js + Leaflet), im Browser testbar
app/            Android-Schale (WebView) — Datei, Kamera, Standort, Karten-App
Archive/        Submodul thee/Archive — daraus nur ArchivClient + automatik.js
tests/          Rauchtest und Schnappschüsse (Playwright)
make-icon.py    erzeugt das Launcher-Icon
make-skizzen.py rechnet die Seitenansichten der Verstellungs-Karten (in web/app.html)

Der Zustand liegt vollständig in der Seite; die Schale kann nur, was eine Web-Seite nicht kann. Gespeichert wird filesDir/motive.json, Fotos liegen in filesDir/fotos und werden über den WebViewAssetLoader als https://appassets.androidplatform.net/fotos/… ausgeliefert.

Ein Motiv hält seine Bilder in fotos[] ({datei, thumb, angelegt, zeit, notiz, ausschnitt} — zeit ist die Aufnahmezeit, angelegt nur der Zeitpunkt der Übernahme), sein Logbuch in log[] ({id, ts, text}) und seine Themen in themen[] — dort stehen nur ids; die Namen liegen einmal in daten.themen[] ({id, name, angelegt}), damit Umbenennen an einer Stelle wirkt und nicht an fünfzig. foto/thumb gibt es weiter als Spiegel des Titelbildes, damit ältere Sicherungen lesbar bleiben und die Schale erkennt, welche Dateien noch gebraucht werden. Verwaiste Dateien werden nicht sofort gelöscht, sondern erst vorgemerkt und nach zehn Minuten — sonst wäre ein Foto in der Sekunde weg, in der man es aus dem Motiv nimmt, und „Rückgängig" holte einen Eintrag ohne Bild zurück.

Aufnahmezeit aus dem EXIF

Die Zeit wird in MainActivity.exifZeit() selbst aus dem Tag gelesen, nicht über ExifInterface.getDateTimeOriginal(): dessen Formatierer steht fest auf UTC, im Bild steht aber Ortszeit ohne Zone — hierzulande kam so aus einem 20-Uhr-Foto eine 22-Uhr-Anzeige (bis einschließlich 1.3.0). Notiert die Kamera die Zone (OffsetTimeOriginal), gilt sie; sonst wird die Zeitzone des Telefons angenommen.

Die bis dahin falsch gemerkten Zeiten rechnet richteZeitenGerade() beim ersten Start von 1.4.0 einmalig zurück (Marke daten.zeitfix). Angefasst wird nur, wo der Fehler beweisbar ist: ein Foto kann nicht nach dem Zeitpunkt aufgenommen worden sein, an dem es in die Liste kam. Bei einem Galeriebild von vorgestern lässt sich nichts zeigen — das bleibt unberührt.

Bauen

JAVA_HOME=/usr/lib/jvm/java-21-temurin-jdk ./gradlew assembleRelease

Ausgeliefert wird über das F-Droid-Repo (apk-publish.sh + deploy-fdroid.sh).

Tests

node tests/rauchtest.mjs     # 196 Prüfungen gegen eine Attrappe der Schale
node tests/bilder.mjs        # Schnappschüsse nach tests/bilder/

Die Attrappe (window.Android) antwortet auf Foto und Standort sofort — damit laufen genau die Wege durch, die auf dem Telefon über die Brücke gehen.

Der Prüfserver liefert Archive/web/automatik.js unter /schale/ aus, so wie es beim Bauen die Kopieraufgabe tut — sonst liefe die Seite im Test gegen ein 404 und die Automatik fehlte lautlos.

Was die App ans Archiv schickt, prüft die Gegenseite:

MO_DATEN_SCHREIBEN=1 node tests/rauchtest.mjs   # schreibt tests/motivdaten.json
cp tests/motivdaten.json Archive/server/tests/  # dort: test_verlustfrei.py