diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5d6b6ccc..e8a8cf54 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -29,6 +29,14 @@
+
+
+
+
+
+
+
+
Unit = { rootBackStack.removeLastOrNull() }
+ val slideInTransition = slideInHorizontally(initialOffsetX = { it }) togetherWith scaleOut(targetScale = 0.9f)
+ val slideOutTransition = scaleIn(initialScale = 0.9f) togetherWith slideOutHorizontally(targetOffsetX = { it })
+ NavDisplay(
+ modifier = Modifier.fillMaxSize(),
+ backStack = rootBackStack,
+ onBack = navigateBack,
+ transitionSpec = { slideInTransition },
+ popTransitionSpec = { slideOutTransition },
+ predictivePopTransitionSpec = { slideOutTransition },
+ entryDecorators = listOf(
+ rememberSaveableStateHolderNavEntryDecorator(), // Required for saving Compose state per entry
+ rememberViewModelStoreNavEntryDecorator() // Required for ViewModel scoping per entry
+ ),
+ entryProvider = entryProvider {
+ entry { MainScreen(navigateToRadar = { rootBackStack.add(RadarDestination) }) }
+ entry {
+ Scaffold { innerPadding ->
+ RadarDestination(navigateUp = navigateBack)
+ innerPadding.calculateTopPadding()
+ }
+ }
+ }
+ )
+}
+
+@Composable
+fun MainScreen(navigateToRadar: () -> Unit = {}) {
val backStack = rememberNavBackStack(Screen.Passes)
val currentKey = backStack.lastOrNull()
- val navigateBack: () -> Unit = { if (backStack.size > 1) backStack.removeAt(backStack.size - 1) }
+ val navigateBack: () -> Unit = { backStack.removeLastOrNull() }
val fadeTransition = fadeIn(animationSpec = tween(350)) togetherWith fadeOut(animationSpec = tween(350))
-// val slideInTransition = slideInHorizontally(initialOffsetX = { it }) togetherWith scaleOut(targetScale = 0.9f)
-// val slideOutTransition = scaleIn(initialScale = 0.9f) togetherWith slideOutHorizontally(targetOffsetX = { it })
val navItems = listOf(Screen.Satellites, Screen.Passes, Screen.Radar, Screen.Map, Screen.Settings)
val context = LocalContext.current
@@ -139,6 +182,7 @@ fun MainScreen() {
PassesDestination { catNum, aosTime ->
container.satelliteRepo.selectPass(catNum, aosTime)
backStack.add(Screen.Radar)
+// navigateToRadar()
}
}
entry {
diff --git a/core/presentation/src/main/java/com/rtbishop/look4sat/core/presentation/Navigation.kt b/core/presentation/src/main/java/com/rtbishop/look4sat/core/presentation/Navigation.kt
index bf4349d8..b0ff0c1b 100644
--- a/core/presentation/src/main/java/com/rtbishop/look4sat/core/presentation/Navigation.kt
+++ b/core/presentation/src/main/java/com/rtbishop/look4sat/core/presentation/Navigation.kt
@@ -38,3 +38,33 @@ sealed class Screen(val iconResId: Int, val titleResId: Int) : NavKey {
@Serializable
data object Settings : Screen(R.drawable.ic_settings, R.string.nav_prefs)
}
+
+@Serializable
+data object RadarDestination : NavKey
+
+interface IDeeplinkMatcher {
+ fun match(deeplink: String): NavKey?
+}
+
+object PassDetailsMatcher : IDeeplinkMatcher {
+ val passDetailsRegex = """https://github.com/rt-bishop/Look4Sat/passes/(.*)""".toRegex()
+
+ override fun match(deeplink: String): NavKey? {
+ val passMatch = passDetailsRegex.find(deeplink)
+ passMatch?.let { match ->
+ val passId = match.groupValues[1]
+ if (passId.isNotEmpty()) return RadarDestination
+ }
+ return null
+ }
+}
+
+class DeeplinkResolver(private val fallbackDestination: NavKey = Screen.Passes) {
+
+ private val matchers: List = listOf(PassDetailsMatcher)
+
+ fun resolve(deeplink: String): NavKey {
+ matchers.forEach { it.match(deeplink)?.let { match -> return match } }
+ return fallbackDestination
+ }
+}
diff --git a/core/presentation/src/test/java/com/rtbishop/look4sat/core/presentation/DeeplinkResolverTest.kt b/core/presentation/src/test/java/com/rtbishop/look4sat/core/presentation/DeeplinkResolverTest.kt
new file mode 100644
index 00000000..9c2738ae
--- /dev/null
+++ b/core/presentation/src/test/java/com/rtbishop/look4sat/core/presentation/DeeplinkResolverTest.kt
@@ -0,0 +1,39 @@
+/*
+ * Look4Sat. Amateur radio satellite tracker and pass predictor.
+ * Copyright (C) 2019-2026 Arty Bishop and contributors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.rtbishop.look4sat.core.presentation
+
+import org.junit.Test
+
+class DeeplinkResolverTest {
+
+ @Test
+ fun returnsDefaultDestination() {
+ val deeplinkResolver = DeeplinkResolver()
+ val result = deeplinkResolver.resolve("/deeplink")
+ assert(result == Screen.Passes)
+ }
+
+ @Test
+ fun returnsRadarDestination() {
+ val passId = "some-pass-id"
+ val passDeeplink = "https://github.com/rt-bishop/Look4Sat/passes/$passId"
+ val deeplinkResolver = DeeplinkResolver()
+ val result = deeplinkResolver.resolve(passDeeplink)
+ assert(result == RadarDestination)
+ }
+}