Release GPS2Audio v2.5.44 POI category fix
This commit is contained in:
@@ -8,14 +8,32 @@ import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import de.waypointaudio.data.TermsStore
|
||||
import de.waypointaudio.data.TermsVersions
|
||||
import de.waypointaudio.data.WakeLockSettingsStore
|
||||
import de.waypointaudio.service.WakeLockController
|
||||
import de.waypointaudio.ui.TermsDialog
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import de.waypointaudio.ui.ExplorerScreen
|
||||
import de.waypointaudio.ui.ExplorerScreenMapLibre
|
||||
import de.waypointaudio.ui.HomeScreen
|
||||
import de.waypointaudio.ui.MapMode
|
||||
import de.waypointaudio.ui.MapScreen
|
||||
import de.waypointaudio.ui.PermissionRequiredScreen
|
||||
import de.waypointaudio.ui.TrackDraftEditorScreen
|
||||
import de.waypointaudio.ui.TrackDraftListScreen
|
||||
import de.waypointaudio.ui.VersionNoticeDialogHost
|
||||
import de.waypointaudio.ui.WaypointListScreen
|
||||
import de.waypointaudio.ui.theme.WaypointAudioTheme
|
||||
import de.waypointaudio.viewmodel.WaypointViewModel
|
||||
@@ -40,6 +58,16 @@ class MainActivity : ComponentActivity() {
|
||||
/** Nur Standortberechtigungen steuern, ob die App-UI angezeigt wird. */
|
||||
private val locationPermissionsGranted = mutableStateOf(false)
|
||||
|
||||
/** Hält das Display abhängig von der Nutzer-Einstellung aktiv. */
|
||||
lateinit var wakeLockController: WakeLockController
|
||||
private set
|
||||
|
||||
/**
|
||||
* v2.5.9 — Terms-Akzeptanz. -1 = noch nicht geladen, 0 = noch nicht akzeptiert
|
||||
* (Dialog zeigen), 1 = akzeptiert (UI freigeben).
|
||||
*/
|
||||
private val termsState = mutableStateOf(-1)
|
||||
|
||||
// Launcher für Standortberechtigungen (zwingend erforderlich)
|
||||
private val locationPermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestMultiplePermissions()
|
||||
@@ -60,31 +88,157 @@ class MainActivity : ComponentActivity() {
|
||||
) { /* optional – kein Block der Hauptoberfläche */ }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// v2.5.8 — Android Splash Screen (androidx.core.splashscreen). MUSS vor
|
||||
// super.onCreate() installiert werden. Sorgt für ein kurzes, dezentes
|
||||
// GPS2Audio-Intro mit Logo (Map-Pin + Wellen) auf Navy-Hintergrund.
|
||||
// Wir halten den Splash bewusst nur ca. 700 ms — kein Loading-Spinner,
|
||||
// keine Werbe-Pause. Auf API 31+ animiert das System das Logo automatisch
|
||||
// beim Übergang. Auf API 26–30 nutzt der Backport dieselbe Optik.
|
||||
val splash = installSplashScreen()
|
||||
val splashStart = android.os.SystemClock.elapsedRealtime()
|
||||
splash.setKeepOnScreenCondition {
|
||||
android.os.SystemClock.elapsedRealtime() - splashStart < 700L
|
||||
}
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Wake-Lock-Controller initialisieren
|
||||
wakeLockController = WakeLockController(this)
|
||||
val wakeStore = WakeLockSettingsStore(applicationContext)
|
||||
lifecycleScope.launchWhenStarted {
|
||||
wakeStore.settings.collect { settings ->
|
||||
wakeLockController.applySettings(settings)
|
||||
}
|
||||
}
|
||||
lifecycleScope.launchWhenStarted {
|
||||
viewModel.serviceRunning.collect { running ->
|
||||
wakeLockController.setTourActive(running)
|
||||
}
|
||||
}
|
||||
|
||||
// v2.5.9 — Terms-Akzeptanz laden. Solange unklar, UI nicht aufdecken.
|
||||
val termsStore = TermsStore(applicationContext)
|
||||
lifecycleScope.launch {
|
||||
val accepted = termsStore.acceptedVersion.first()
|
||||
termsState.value = if (accepted == TermsVersions.CURRENT) 1 else 0
|
||||
}
|
||||
|
||||
// Standortberechtigungen prüfen und ggf. anfordern
|
||||
checkAndRequestLocationPermissions()
|
||||
|
||||
setContent {
|
||||
WaypointAudioTheme(darkTheme = isSystemInDarkTheme()) {
|
||||
val locationGranted by locationPermissionsGranted
|
||||
val termsAccepted by termsState
|
||||
|
||||
if (locationGranted) {
|
||||
// v2.5.9 — Solange der Akzeptanzstatus noch nicht geladen wurde
|
||||
// (termsAccepted == -1) zeigen wir nichts; das verhindert ein
|
||||
// kurzes "Aufflackern" der UI bevor der Dialog erscheint.
|
||||
if (termsAccepted == -1) {
|
||||
// bewusst leeres Theme — DataStore ist nach wenigen ms da.
|
||||
} else if (termsAccepted == 0) {
|
||||
TermsDialog(
|
||||
onAccept = {
|
||||
lifecycleScope.launch {
|
||||
termsStore.setAccepted(TermsVersions.CURRENT)
|
||||
termsState.value = 1
|
||||
}
|
||||
},
|
||||
onDecline = {
|
||||
// Ohne Zustimmung darf die App nicht weiter genutzt werden.
|
||||
finishAndRemoveTask()
|
||||
}
|
||||
)
|
||||
} else if (locationGranted) {
|
||||
val navController = rememberNavController()
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = "list"
|
||||
startDestination = "home"
|
||||
) {
|
||||
// v2.5.0 — neue Hauptseite "Audio-Bibliothek".
|
||||
composable("home") {
|
||||
HomeScreen(
|
||||
viewModel = viewModel,
|
||||
onOpenTour = { _ ->
|
||||
// Tour wurde im HomeScreen bereits über
|
||||
// viewModel.selectTour() aktiviert. Wir öffnen
|
||||
// direkt die bestehende Tour-/Waypoint-Ansicht.
|
||||
navController.navigate("list")
|
||||
},
|
||||
// v2.5.34 — Explorer-Kontext-Fix:
|
||||
// Einstieg aus Audio-Bibliothek/Home muss immer neutral
|
||||
// öffnen. selectDefaultTour() setzt selectedTour auf
|
||||
// DEFAULT_TOUR_NAME, bevor der Explorer gerendert wird.
|
||||
// Einstieg aus WaypointListScreen (Tour-Ansicht) behält
|
||||
// die jeweils aktive Tour — dort wird selectDefaultTour()
|
||||
// nicht aufgerufen.
|
||||
onOpenExplorer = {
|
||||
viewModel.selectDefaultTour()
|
||||
navController.navigate("explorer")
|
||||
},
|
||||
// v2.5.19 — „Track Editor" wurde aus dem Hauptmenü
|
||||
// entfernt; Drafts bleiben über den Explorer
|
||||
// erreichbar. Die „drafts"-Route bleibt für den
|
||||
// Explorer-Fluss bestehen.
|
||||
onOpenClassicView = { navController.navigate("list") }
|
||||
)
|
||||
}
|
||||
composable("list") {
|
||||
WaypointListScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToMap = { navController.navigate("map") }
|
||||
onNavigateToMap = { navController.navigate("map") },
|
||||
onNavigateToDrafts = { navController.navigate("drafts") },
|
||||
onNavigateToExplorer = { navController.navigate("explorer") }
|
||||
)
|
||||
}
|
||||
composable("map") {
|
||||
// v2.2.3: Legacy-Editor — nur noch als interner Fallback erreichbar.
|
||||
// Mode-Switch zwischen Karteneditor und Explorer entfällt, Explorer
|
||||
// ist der zentrale Arbeitsbereich.
|
||||
MapScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
currentMode = null,
|
||||
onModeChange = null
|
||||
)
|
||||
}
|
||||
composable("explorer") {
|
||||
// v2.4.0 — MapLibre Engine Cutover: aktiver Renderer ist jetzt MapLibre.
|
||||
// Die osmdroid-Variante in [ExplorerScreen] bleibt im Quelltext als
|
||||
// dokumentierter Fallback erhalten.
|
||||
ExplorerScreenMapLibre(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
onOpenInEditor = { /* abgelöst — Explorer übernimmt */ },
|
||||
onOpenDraft = { id -> navController.navigate("draft/$id") }
|
||||
)
|
||||
// Hinweis: ExplorerScreen (osmdroid) bleibt aus Build-/Symbol-
|
||||
// Kompatibilität in der Sourcetree.
|
||||
@Suppress("unused")
|
||||
val osmdroidFallbackRef: @androidx.compose.runtime.Composable () -> Unit = {
|
||||
ExplorerScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
onOpenInEditor = { /* abgelöst — Explorer übernimmt */ },
|
||||
onOpenDraft = { id -> navController.navigate("draft/$id") }
|
||||
)
|
||||
}
|
||||
}
|
||||
composable("drafts") {
|
||||
TrackDraftListScreen(
|
||||
viewModel = viewModel,
|
||||
onOpenDraft = { id -> navController.navigate("draft/$id") },
|
||||
onNavigateBack = { navController.popBackStack() }
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = "draft/{draftId}",
|
||||
arguments = listOf(navArgument("draftId") { type = NavType.StringType })
|
||||
) { backStackEntry ->
|
||||
val id = backStackEntry.arguments?.getString("draftId") ?: ""
|
||||
TrackDraftEditorScreen(
|
||||
viewModel = viewModel,
|
||||
draftId = id,
|
||||
onNavigateBack = { navController.popBackStack() }
|
||||
)
|
||||
}
|
||||
@@ -105,6 +259,16 @@ class MainActivity : ComponentActivity() {
|
||||
if (locationPermissionsGranted.value) {
|
||||
requestOptionalPermissions()
|
||||
}
|
||||
if (::wakeLockController.isInitialized) {
|
||||
wakeLockController.onActivityResumed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
if (::wakeLockController.isInitialized) {
|
||||
wakeLockController.onActivityPaused()
|
||||
}
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user