package de.waypointaudio.license /** * v2.5.32 — Zentrale Feature-Berechtigungsprüfung. * * Diese Klasse kapselt alle Regeln, welche Lizenztypen Zugriff auf * Profi-Funktionen gewähren. Sie liest ausschließlich aus [LicenseStatus] * und ändert NIEMALS Lizenzdaten, Import-Logik, Signaturprüfung, * Geräte-ID, CanonicalJson oder Backup-Regeln. * * Lizenztypen mit professionellem Zugang: * - "pro", "profi", "commercial" → Profi-Zugang solange status == VALID * - "developer" → immer erlaubt wenn status == VALID * - "trial" → Profi-Zugang solange status == VALID (valid_until-Prüfung * erfolgt bereits in LicenseValidator; ist abgelaufen → state == EXPIRED) * * Ohne Lizenz (state == NONE) oder mit abgelaufener/ungültiger Lizenz: * - Keine Profi-Funktion freigegeben. * - Vorhandene Daten (Zusatzclips, PTT-Aufnahmen, Zählerwerte) werden * NIEMALS gelöscht. * * Feature-Gates: * - [hasProfessionalAccess]: gemeinsames Haupt-Gate * - [canUseMultiClip]: Zusatzclips bearbeiten/hinzufügen/aktivieren * - [canUsePtt]: PTT-Modul anzeigen/nutzen * - [canUseTourCounter]: Tour-Zähler anzeigen/nutzen * * UI-Empfehlung: Prüfe den Status über [LicenseStatusProvider.status], das * als Compose-State vorliegt. Reagiert reaktiv auf Lizenzimport ohne * App-Neustart. */ object LicenseFeatureAccess { /** * Professionelle Lizenztypen (case-insensitive), die Profi-Zugang gewähren. */ private val PRO_LICENSE_TYPES = setOf("pro", "profi", "commercial", "developer", "trial") /** * Prüft, ob die aktuelle Lizenz professionellen Zugang gewährt. * * Bedingungen: * 1. [LicenseStatus.state] muss [LicenseStatus.State.VALID] sein. * 2. [LicenseStatus.licenseTypeRaw] muss in [PRO_LICENSE_TYPES] enthalten sein * (case-insensitive), ODER in [LicenseStatus.permissions] ist mindestens * eine Profi-Permission gesetzt. * 3. Developer-Lizenzen sind immer eingeschlossen, wenn state == VALID. */ fun hasProfessionalAccess(status: LicenseStatus): Boolean { if (status.state != LicenseStatus.State.VALID) return false val typeRaw = status.licenseTypeRaw?.lowercase()?.trim() ?: "" if (typeRaw in PRO_LICENSE_TYPES) return true // Fallback: explizite Permissions (z. B. "commercial_use" oder "paid_tours") return status.permissions.any { (_, v) -> v } } /** * Shortcut: Liest den aktuellen Status direkt aus [LicenseStatusProvider]. * Für Compose-Code bevorzuge den Status-Parameter, damit die Rekomposition * reaktiv funktioniert. */ fun hasProfessionalAccessCurrent(): Boolean = hasProfessionalAccess(LicenseStatusProvider.current()) /** * Multi-Clip-Waypoints: Bearbeiten, Hinzufügen, Aktivieren von Zusatzclips. * Hauptclip (index 0 / isPrimary) bleibt immer frei. * Bestehende Clips werden niemals gelöscht. */ fun canUseMultiClip(status: LicenseStatus): Boolean = hasProfessionalAccess(status) /** * PTT (Push-to-Talk): Live-PTT-Modul, Aufnahmen starten. * Bestehende PTT-Aufnahmen bleiben erhalten. */ fun canUsePtt(status: LicenseStatus): Boolean = hasProfessionalAccess(status) /** * Tour-Zähler: Abspiel-Modi, Zähler-Reset, Vorgaben. * Bestehende Zählerwerte bleiben erhalten. */ fun canUseTourCounter(status: LicenseStatus): Boolean = hasProfessionalAccess(status) /** * Freundlicher Hinweistext für das Multi-Clip-Gate. */ const val MSG_MULTI_CLIP = "Multi-Clip-Waypoints sind Teil der GPS2Audio Profi-Lizenz." /** * Freundlicher Hinweistext für das PTT-Gate. */ const val MSG_PTT = "Live-PTT ist Teil der GPS2Audio Profi-Lizenz." /** * Freundlicher Hinweistext für das Tour-Zähler-Gate. */ const val MSG_TOUR_COUNTER = "Der Tour-Zähler ist Teil der GPS2Audio Profi-Lizenz." }