245 lines
13 KiB
Kotlin
245 lines
13 KiB
Kotlin
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/<tour>.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 = 102
|
||
versionName = "2.5.48"
|
||
}
|
||
|
||
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)
|
||
}
|