Release GPS2Audio v2.5.44 POI category fix
This commit is contained in:
@@ -38,8 +38,21 @@ class WaypointStore(private val context: Context) {
|
||||
private val KEY_TOURS = stringPreferencesKey("tours_json")
|
||||
private val KEY_TOUR_DEFAULTS = stringPreferencesKey("tour_playback_defaults_json")
|
||||
private val KEY_GPS_TRACKS = stringPreferencesKey("gps_tracks_json")
|
||||
private val KEY_TRACK_DRAFTS = stringPreferencesKey("track_drafts_json")
|
||||
// v2.4.6 — persistente Tourrouten (tourName → TourRoute, JSON).
|
||||
// Eigene Schlüssel-Datei: bestehende Backups/JSON ohne diesen Key
|
||||
// bleiben gültig (Default = leere Map).
|
||||
private val KEY_TOUR_ROUTES = stringPreferencesKey("tour_routes_json")
|
||||
// v2.5.5 — Persistente Tour-Cover (tourName → TourCover, JSON).
|
||||
// Optional pro Tour; fehlt der Eintrag, bleibt die deterministische
|
||||
// Standard-Optik (Akzentfarbe + Icon-Heuristik aus Tournamen) erhalten.
|
||||
private val KEY_TOUR_COVERS = stringPreferencesKey("tour_covers_json")
|
||||
}
|
||||
|
||||
private val draftListType = object : TypeToken<List<TrackDraft>>() {}.type
|
||||
private val tourRoutesMapType = object : TypeToken<Map<String, TourRoute>>() {}.type
|
||||
private val tourCoversMapType = object : TypeToken<Map<String, TourCover>>() {}.type
|
||||
|
||||
/** Liefert einen Flow mit der aktuellen Wegpunktliste. */
|
||||
val waypointsFlow: Flow<List<Waypoint>> = context.dataStore.data.map { prefs ->
|
||||
val json = prefs[KEY_WAYPOINTS] ?: "[]"
|
||||
@@ -72,6 +85,33 @@ class WaypointStore(private val context: Context) {
|
||||
}.getOrDefault(emptyMap())
|
||||
}
|
||||
|
||||
/**
|
||||
* v2.4.6 — Persistente Tourrouten als Map (tourName → TourRoute).
|
||||
* Rückwärtskompatibel: fehlt der Key komplett, ist die Map leer.
|
||||
* Auch korrupte JSON werden defensiv auf leer abgefangen, damit eine
|
||||
* einzelne kaputte Route nicht den Rest der App blockiert.
|
||||
*/
|
||||
val tourRoutesFlow: Flow<Map<String, TourRoute>> =
|
||||
context.dataStore.data.map { prefs ->
|
||||
val json = prefs[KEY_TOUR_ROUTES] ?: "{}"
|
||||
runCatching {
|
||||
gson.fromJson<Map<String, TourRoute>>(json, tourRoutesMapType) ?: emptyMap()
|
||||
}.getOrDefault(emptyMap())
|
||||
}
|
||||
|
||||
/**
|
||||
* v2.5.5 — Persistente Tour-Cover als Map (tourName → TourCover).
|
||||
* Rückwärtskompatibel: fehlt der Key, ist die Map leer und Touren
|
||||
* bekommen ihre bisherige deterministische Hero-Optik.
|
||||
*/
|
||||
val tourCoversFlow: Flow<Map<String, TourCover>> =
|
||||
context.dataStore.data.map { prefs ->
|
||||
val json = prefs[KEY_TOUR_COVERS] ?: "{}"
|
||||
runCatching {
|
||||
gson.fromJson<Map<String, TourCover>>(json, tourCoversMapType) ?: emptyMap()
|
||||
}.getOrDefault(emptyMap())
|
||||
}
|
||||
|
||||
/** GPS-Track-Flow: Map tour → Liste von TrackPoint. */
|
||||
val gpsTracksFlow: Flow<Map<String, List<GpsTrackPoint>>> =
|
||||
context.dataStore.data.map { prefs ->
|
||||
@@ -82,6 +122,59 @@ class WaypointStore(private val context: Context) {
|
||||
}.getOrDefault(emptyMap())
|
||||
}
|
||||
|
||||
/**
|
||||
* Reaktiver Flow der archivierten Track-Drafts.
|
||||
* Drafts sind eigenständige, persistierte Aufzeichnungen — neue
|
||||
* Aufzeichnungen werden als zusätzliche Einträge gespeichert,
|
||||
* niemals durch Überschreiben.
|
||||
*/
|
||||
val trackDraftsFlow: Flow<List<TrackDraft>> = context.dataStore.data.map { prefs ->
|
||||
val json = prefs[KEY_TRACK_DRAFTS] ?: "[]"
|
||||
runCatching {
|
||||
gson.fromJson<List<TrackDraft>>(json, draftListType) ?: emptyList()
|
||||
}.getOrDefault(emptyList())
|
||||
}
|
||||
|
||||
/** Fügt einen neuen Track-Draft hinzu (immer als neuen Eintrag). */
|
||||
suspend fun addTrackDraft(draft: TrackDraft) {
|
||||
context.dataStore.edit { prefs ->
|
||||
val json = prefs[KEY_TRACK_DRAFTS] ?: "[]"
|
||||
val list: MutableList<TrackDraft> = runCatching {
|
||||
gson.fromJson<List<TrackDraft>>(json, draftListType)?.toMutableList()
|
||||
}.getOrNull() ?: mutableListOf()
|
||||
list.add(draft)
|
||||
prefs[KEY_TRACK_DRAFTS] = gson.toJson(list)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert einen vorhandenen Draft anhand seiner ID.
|
||||
* Falls noch nicht vorhanden, wird er hinzugefügt (defensiv).
|
||||
*/
|
||||
suspend fun upsertTrackDraft(draft: TrackDraft) {
|
||||
context.dataStore.edit { prefs ->
|
||||
val json = prefs[KEY_TRACK_DRAFTS] ?: "[]"
|
||||
val list: MutableList<TrackDraft> = runCatching {
|
||||
gson.fromJson<List<TrackDraft>>(json, draftListType)?.toMutableList()
|
||||
}.getOrNull() ?: mutableListOf()
|
||||
val idx = list.indexOfFirst { it.id == draft.id }
|
||||
if (idx >= 0) list[idx] = draft else list.add(draft)
|
||||
prefs[KEY_TRACK_DRAFTS] = gson.toJson(list)
|
||||
}
|
||||
}
|
||||
|
||||
/** Löscht einen Track-Draft anhand seiner ID. */
|
||||
suspend fun deleteTrackDraft(id: String) {
|
||||
context.dataStore.edit { prefs ->
|
||||
val json = prefs[KEY_TRACK_DRAFTS] ?: "[]"
|
||||
val list: MutableList<TrackDraft> = runCatching {
|
||||
gson.fromJson<List<TrackDraft>>(json, draftListType)?.toMutableList()
|
||||
}.getOrNull() ?: mutableListOf()
|
||||
list.removeAll { it.id == id }
|
||||
prefs[KEY_TRACK_DRAFTS] = gson.toJson(list)
|
||||
}
|
||||
}
|
||||
|
||||
/** Speichert die gesamte Wegpunktliste. */
|
||||
suspend fun saveAll(waypoints: List<Waypoint>) {
|
||||
context.dataStore.edit { prefs ->
|
||||
@@ -131,6 +224,125 @@ class WaypointStore(private val context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* v2.4.6 — Speichert (oder ersetzt) die gespeicherte Route einer Tour.
|
||||
* Setzt [TourRoute.updatedAtMillis] auf die aktuelle Zeit, sofern das
|
||||
* übergebene Objekt nicht selbst einen neueren Zeitstempel mitbringt.
|
||||
*/
|
||||
suspend fun saveTourRoute(tourName: String, route: TourRoute) {
|
||||
context.dataStore.edit { prefs ->
|
||||
val json = prefs[KEY_TOUR_ROUTES] ?: "{}"
|
||||
val map: MutableMap<String, TourRoute> = runCatching {
|
||||
gson.fromJson<Map<String, TourRoute>>(json, tourRoutesMapType)?.toMutableMap()
|
||||
}.getOrNull() ?: mutableMapOf()
|
||||
val stamped = route.copy(
|
||||
updatedAtMillis = if (route.updatedAtMillis > 0) route.updatedAtMillis
|
||||
else System.currentTimeMillis()
|
||||
)
|
||||
map[tourName] = stamped
|
||||
prefs[KEY_TOUR_ROUTES] = gson.toJson(map)
|
||||
}
|
||||
}
|
||||
|
||||
/** Entfernt die gespeicherte Route einer Tour (falls vorhanden). */
|
||||
suspend fun deleteTourRoute(tourName: String) {
|
||||
context.dataStore.edit { prefs ->
|
||||
val json = prefs[KEY_TOUR_ROUTES] ?: "{}"
|
||||
val map: MutableMap<String, TourRoute> = runCatching {
|
||||
gson.fromJson<Map<String, TourRoute>>(json, tourRoutesMapType)?.toMutableMap()
|
||||
}.getOrNull() ?: mutableMapOf()
|
||||
if (map.remove(tourName) != null) {
|
||||
prefs[KEY_TOUR_ROUTES] = gson.toJson(map)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Benennt die Tour einer gespeicherten Route um. Wird aus
|
||||
* [WaypointViewModel.renameTour]/[deleteTour] aufgerufen, damit die
|
||||
* Route nicht verwaist zurückbleibt.
|
||||
*/
|
||||
suspend fun renameTourRouteKey(oldTour: String, newTour: String) {
|
||||
if (oldTour == newTour) return
|
||||
context.dataStore.edit { prefs ->
|
||||
val json = prefs[KEY_TOUR_ROUTES] ?: "{}"
|
||||
val map: MutableMap<String, TourRoute> = runCatching {
|
||||
gson.fromJson<Map<String, TourRoute>>(json, tourRoutesMapType)?.toMutableMap()
|
||||
}.getOrNull() ?: mutableMapOf()
|
||||
val existing = map.remove(oldTour) ?: return@edit
|
||||
map[newTour] = existing.copy(updatedAtMillis = System.currentTimeMillis())
|
||||
prefs[KEY_TOUR_ROUTES] = gson.toJson(map)
|
||||
}
|
||||
}
|
||||
|
||||
/** Bulk-Schreiben der Routen (für Backup-Import). Vorhandene Keys werden überschrieben. */
|
||||
suspend fun mergeTourRoutes(routes: Map<String, TourRoute>) {
|
||||
if (routes.isEmpty()) return
|
||||
context.dataStore.edit { prefs ->
|
||||
val json = prefs[KEY_TOUR_ROUTES] ?: "{}"
|
||||
val map: MutableMap<String, TourRoute> = runCatching {
|
||||
gson.fromJson<Map<String, TourRoute>>(json, tourRoutesMapType)?.toMutableMap()
|
||||
}.getOrNull() ?: mutableMapOf()
|
||||
map.putAll(routes)
|
||||
prefs[KEY_TOUR_ROUTES] = gson.toJson(map)
|
||||
}
|
||||
}
|
||||
|
||||
/** v2.5.5 — Speichert das Cover einer Tour (überschreibt vorhandenes). */
|
||||
suspend fun saveTourCover(tourName: String, cover: TourCover) {
|
||||
context.dataStore.edit { prefs ->
|
||||
val json = prefs[KEY_TOUR_COVERS] ?: "{}"
|
||||
val map: MutableMap<String, TourCover> = runCatching {
|
||||
gson.fromJson<Map<String, TourCover>>(json, tourCoversMapType)?.toMutableMap()
|
||||
}.getOrNull() ?: mutableMapOf()
|
||||
map[tourName] = cover
|
||||
prefs[KEY_TOUR_COVERS] = gson.toJson(map)
|
||||
}
|
||||
}
|
||||
|
||||
/** v2.5.5 — Entfernt das Cover einer Tour (falls vorhanden). */
|
||||
suspend fun deleteTourCover(tourName: String) {
|
||||
context.dataStore.edit { prefs ->
|
||||
val json = prefs[KEY_TOUR_COVERS] ?: "{}"
|
||||
val map: MutableMap<String, TourCover> = runCatching {
|
||||
gson.fromJson<Map<String, TourCover>>(json, tourCoversMapType)?.toMutableMap()
|
||||
}.getOrNull() ?: mutableMapOf()
|
||||
if (map.remove(tourName) != null) {
|
||||
prefs[KEY_TOUR_COVERS] = gson.toJson(map)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* v2.5.5 — Tour-Umbenennung: Cover-Eintrag mitziehen, damit er nicht
|
||||
* unter einem verwaisten Tournamen liegen bleibt.
|
||||
*/
|
||||
suspend fun renameTourCoverKey(oldTour: String, newTour: String) {
|
||||
if (oldTour == newTour) return
|
||||
context.dataStore.edit { prefs ->
|
||||
val json = prefs[KEY_TOUR_COVERS] ?: "{}"
|
||||
val map: MutableMap<String, TourCover> = runCatching {
|
||||
gson.fromJson<Map<String, TourCover>>(json, tourCoversMapType)?.toMutableMap()
|
||||
}.getOrNull() ?: mutableMapOf()
|
||||
val existing = map.remove(oldTour) ?: return@edit
|
||||
map[newTour] = existing
|
||||
prefs[KEY_TOUR_COVERS] = gson.toJson(map)
|
||||
}
|
||||
}
|
||||
|
||||
/** v2.5.5 — Bulk-Schreiben der Cover (für Backup-Import). */
|
||||
suspend fun mergeTourCovers(covers: Map<String, TourCover>) {
|
||||
if (covers.isEmpty()) return
|
||||
context.dataStore.edit { prefs ->
|
||||
val json = prefs[KEY_TOUR_COVERS] ?: "{}"
|
||||
val map: MutableMap<String, TourCover> = runCatching {
|
||||
gson.fromJson<Map<String, TourCover>>(json, tourCoversMapType)?.toMutableMap()
|
||||
}.getOrNull() ?: mutableMapOf()
|
||||
map.putAll(covers)
|
||||
prefs[KEY_TOUR_COVERS] = gson.toJson(map)
|
||||
}
|
||||
}
|
||||
|
||||
/** Löscht den GPS-Track einer Tour. */
|
||||
suspend fun clearGpsTrack(tourName: String) {
|
||||
context.dataStore.edit { prefs ->
|
||||
|
||||
Reference in New Issue
Block a user