import java.util.Properties import java.io.FileInputStream plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) } val keystorePropertiesFile = rootProject.file("keystore.properties") val keystoreProperties = Properties().apply { if (keystorePropertiesFile.exists()) { load(FileInputStream(keystorePropertiesFile)) } } fun resolveSigningValue(propsKey: String, envKey: String): String? { val fromProps = keystoreProperties.getProperty(propsKey) if (!fromProps.isNullOrBlank()) return fromProps val fromEnv = System.getenv(envKey) if (!fromEnv.isNullOrBlank()) return fromEnv return null } android { namespace = "de.waypointaudio" compileSdk = 35 buildToolsVersion = "35.0.0" defaultConfig { applicationId = "de.waypointaudio" minSdk = 26 targetSdk = 35 // v2.5.18 — Bugfix: GPX-Import ist jetzt strikt additiv. // - Datenverlust-Schutz: Der GPX-Import in v2.5.17 hat über // `repository.saveAll(imported)` versehentlich alle bestehenden // Wegpunkte in sämtlichen Touren ersetzt. Ab v2.5.18 löscht ein // GPX-Import nie bestehende Daten. // - Neuer Import-Dialog mit zwei Optionen: // (A) Als neue Tour importieren (editierbarer Name, Default aus // GPX-Dateiname). // (B) Zu bestehender Tour hinzufügen (Auswahl der Tour). // Abbrechen verändert nichts. // - JSON-Import/-Export in der sichtbaren UI entfernt; das ZIP-Backup // übernimmt diese Rolle. Interne JSON-Parser/-Modelle bleiben für // Backup-Kompatibilität erhalten. // - v2.5.17 Lizenz/Pro/About bleibt unverändert. // - Alle weiteren Funktionen (Multi-Clip, Sequenzplayer, Countdown, // maximierbarer Player, Tour duplizieren, Waypoint kopieren, // Explorer/MapLibre/Routing, Backup ZIP, PTT/Atmo, Hintergrund, // Tourcover) bleiben unverändert. // // v2.5.19 — Menü- und UX-Politur: // • „Track Editor" aus dem Hauptmenü entfernt (über Explorer // erreichbar). Funktionalität (TrackDraftListScreen/-Editor) // bleibt vollständig im Code. // • „Klassische Ansicht" umbenannt zu „Bearbeitungsmodus" — bessere // UX-Begriffswahl auf Deutsch. // • „Touren ordnen" aus der einzelnen Touransicht in das Menü der // Audio-Bibliothek (HomeScreen) verschoben. Sortier-Funktion // (TourReorderDialog + WaypointViewModel.moveTour) unverändert. // • Backup: Tour-Cover (Icon + Palette + Bild) sind ab v2.5.7 // bereits Teil des ZIP-Backups (media/covers/.jpg) und // werden beim Import wiederhergestellt. v2.5.19 ist hierbei // inhaltlich unverändert; dieser Eintrag dient der Klarstellung // in der Versionshistorie. // // v2.5.20 — Phase 2 des Lizenzsystems: // • Echter Import einer .gps2audio-license-Datei über das Android // Storage Access Framework (ACTION_OPEN_DOCUMENT). // • Offline-Signaturprüfung (ECDSA P-256 SHA-256) gegen den bereits // in res/raw eingebetteten Public Key, kanonisches JSON wie im // Python-Tool (sortierte Keys, kein Whitespace). // • Lokale Ablage der validierten Lizenz in filesDir/license/ // inkl. Backup-Ausschluss (data_extraction_rules + backup_rules). // • Anzeige im About-Dialog: Status (Keine/Gültig/Ungültig/Abgelaufen), // Lizenztyp, Lizenznehmer, Gültigkeit, Lizenz-ID, Bindung, // Permissions (deutsch beschriftet). // • Buttons „Lizenz importieren / exportieren / entfernen" inkl. // Bestätigungsdialog. Keine Feature-Sperren, alle bestehenden // Funktionen bleiben unverändert nutzbar. // v2.5.21 — Re-Import-Bugfix + Geräte-ID: // • Behebt: Lizenzdatei konnte nach Entfernen + Re-Import nicht mehr // gelesen werden. Ursache: LicenseStatusProvider.initialized-Flag // wurde nach remove() nicht zurückgesetzt; kombiniert mit // transienten SAF-URI-Permissions führte das zu "Lizenzdatei konnte // nicht gelesen werden" beim zweiten Import-Versuch. // • Fix: initialized wird in remove() auf false gesetzt. import() // liest Bytes immer frisch, ohne gecachte URIs. // • Geräte-ID: app-spezifische UUID (SharedPreferences, kein Backup), // SHA-256-Hash "GPS2AudioDevice:"+uuid für Lizenz-Tool-Kompatibilität. // Anzeige + Kopier-Button im Über-Dialog. Keine harte Bindung aktiv. // v2.5.23 — Lizenzimport-Regression-Fix: // • v2.5.22 wechselte zu GetContent(), was auf manchen OEM-Geräten // (Huawei/EMUI, MIUI-Galerie-Modus) nur Medien anzeigt — .gps2audio- // license-Dateien unsichtbar, Erstimport unmöglich. // • Fix: Zurück zu OpenDocument() mit breiten MIME-Typen (json/text/ // octet-stream/*). Echter SAF-Datei-Browser zeigt alle Dateien. // • importNonce-Mechanismus: Key-basierter Launcher-Slot-Reset erzwingt // frischen SAF-Picker auch bei Wiederwahl derselben URI. // • Robustes Lesen (copyTo statt readBytes), differenzierte // Fehlermeldungen und reloadTrigger aus v2.5.22 beibehalten. // v2.5.24 — Lizenzimport-Fallbacks: // • readLicenseBytes(): drei Lesestrategien (openInputStream, openFileDescriptor, // File-Fallback) mit separater Fehlersammlung. // • Diagnose-Dialog bei Totalausfall: technische Fehlermeldungen statt generisches Toast. // • Textimport-Fallback: Button "Lizenz aus Zwischenablage / Text einfügen" öffnet // AlertDialog mit großem Textfeld + Zwischenablage-Schaltfläche. // • OpenDocument() mit nur "*/*" (keine MIME-Liste, verhindert Galerie-Modus auf MIUI). // • importNonce-Mechanismus aus v2.5.23 unverändert beibehalten. // v2.5.25 — License-Import-Thread-Fix: // • Alle blockierenden I/O-Operationen (contentResolver.openInputStream, // openFileDescriptor, FileInputStream, ByteArrayOutputStream.copyTo, // LicenseStatusProvider.import inkl. JSON/Signaturvalidierung) auf // Dispatchers.IO verschoben. Root Cause: NetworkOnMainThreadException // auf Android 9+ (StrictMode blockiert Netzwerk-/Disk-I/O auf Main-Thread). // • ActivityResult-Callback startet Coroutine (rememberCoroutineScope); // withContext(Dispatchers.IO) für readLicenseBytes + import(); // danach UI-State/Toast/reloadTrigger auf Main-Thread. // • isImporting-Flag verhindert Doppelauslösung des Import-Buttons. // • Export-Callback (openOutputStream) ebenfalls auf IO. // • Textimport: import() ebenfalls auf IO. // • Alle v2.5.24-Fallbacks (drei Lesestrategien, Diagnose-Dialog, // Textimport, OpenDocument */*, importNonce) unverändert. // v2.5.29 — Lizenz-Persistenz-Fix: Update-sichere Lizenzablage. // v2.5.30 — Home Atmo-Modul entfernt; Playback-Persistenz beim Tour-Wechsel. // • Atmo-Modul/-Toggle auf der Startseite (HomeScreen) entfernt. // Atmo bleibt vollständig in der Tour-Ansicht (WaypointListScreen) verfügbar. // • selectTour() stoppt keine laufende Wiedergabe mehr beim Tour-Wechsel. // Stop nur noch bei expliziten Nutzeraktionen: Play in anderer Tour, // Stop-Button, GPS-Dienst bewusst stoppen. // // v2.5.31 — Tourcover-Backup-Fix: frei gewählte Bilder werden jetzt // zuverlässig gesichert und beim Import wiederhergestellt. // • BackupExportManager: ZIP-Entry-Pfade für Cover-Bilder erhalten // einen eindeutigen Index-Präfix ("media/covers/001_tourname.jpg"), // damit Touren mit gleichem sanitisierten Namen nicht kollidieren. // • BackupExportManager: Fallback-Lesestrategie für Cover-Bilder: // absoluter Dateipfad → ContentResolver → file://-URI. // • BackupImportManager: Verbesserte Fehlerprotokollierung beim // Cover-Bild-Import; Rückwärtskompatibel mit v2.5.7–v2.5.30-Backups. // • Kein Änderungen am Lizenzsystem, Feature-Gates, Touren/Waypoints. // v2.5.34 — Explorer-Kontext-Fix: selectDefaultTour() beim Einstieg aus // der Audio-Bibliothek/Home verhindert das Übernehmen des zuletzt aktiven // Tour-Kontexts. Einstieg aus konkreter Tour behält Tour-Karte. // v2.5.38 — Kartenattribution, POI-Layer, Kartenstil-Persistenz. // • Kartenattribution rechtlich sauber: kompaktes Badge bottom-left // (anklickbar → openstreetmap.org/copyright), vollständiger Karteninfo- // Dialog aus dem Ebenen-Sheet (OpenFreeMap, OpenMapTiles, OSM, ODbL). // • POI-Layer-Kategorien im Ebenen-Sheet: Sehenswürdigkeiten (default an), // Infrastruktur, Gastronomie, Mobilität (default aus). Symbol-Layer des // geladenen MapLibre-Styles werden per Layer-ID-Heuristik ein-/ausgeblendet. // Auswahl wird persistent gespeichert (DataStore). Kein Crash wenn Layer fehlt. // • Kartenstil-Persistenz: baseStyle zuverlässig beim Neustart wiederhergestellt. // POI-Auswahl ebenfalls persistent. Alle MapStyleStore-Keys unverändert. // • Stylewechsel stellt Overlays, Attribution und POI-Sichtbarkeit wieder her. // • Keine Regressionen: Phantom-Layer-Fix, Lizenzsystem, Multi-Clip, // PTT, Tour-Zähler, Backup/Cover, GPX-Import, Explorer/Routing, // Tour-/Waypoint-Kopien, Hintergrundwiedergabe. versionCode = 99 versionName = "2.5.45" } val storeFilePath = resolveSigningValue("storeFile", "GPS2AUDIO_RELEASE_STORE_FILE") val storePassword = resolveSigningValue("storePassword", "GPS2AUDIO_RELEASE_STORE_PASSWORD") val keyAliasValue = resolveSigningValue("keyAlias", "GPS2AUDIO_RELEASE_KEY_ALIAS") val keyPasswordValue = resolveSigningValue("keyPassword", "GPS2AUDIO_RELEASE_KEY_PASSWORD") val hasReleaseSigning = storeFilePath != null && storePassword != null && keyAliasValue != null && keyPasswordValue != null signingConfigs { if (hasReleaseSigning) { create("release") { storeFile = file(storeFilePath!!) this.storePassword = storePassword keyAlias = keyAliasValue keyPassword = keyPasswordValue } } } buildTypes { release { if (hasReleaseSigning) { signingConfig = signingConfigs.getByName("release") } isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { jvmTarget = "17" } buildFeatures { compose = true } lint { abortOnError = false checkReleaseBuilds = false } } dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.lifecycle.viewmodel.compose) implementation(libs.androidx.activity.compose) implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.ui) implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) implementation(libs.androidx.material.icons.extended) implementation(libs.androidx.navigation.compose) implementation(libs.androidx.datastore.preferences) implementation(libs.gson) implementation(libs.kotlinx.coroutines.android) implementation(libs.kotlinx.coroutines.play.services) implementation(libs.play.services.location) implementation(libs.osmdroid) // v2.2.2 — MapLibre Native Android SDK als Build-Abhängigkeit eingezogen. // Das eigentliche Rendering nutzt weiterhin osmdroid (siehe ExplorerScreen), // damit Stabilität/Größe nicht regressieren. MapLibre dient als Grundlage // für künftige Vektor-/3D-Layer-Schritte (siehe Handoff). implementation(libs.maplibre.android.sdk) implementation(libs.media3.exoplayer) implementation(libs.media3.common) // v2.5.8 — Android Splash Screen API (Backport für API 26+). Wir nutzen // den stabilen Weg via androidx.core.splashscreen, damit Pre-31 dieselbe // Intro-Optik bekommt wie API 31+. implementation(libs.androidx.core.splashscreen) debugImplementation(libs.androidx.ui.tooling) }