mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Added initial code for possible future deeplink nav support
This commit is contained in:
1 parent
e5033cb2e9
commit
ec1a55c93d
4 files changed
+125
-4
No files matched your search
@@ -29,6 +29,14 @@
|
|||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<!-- <intent-filter android:autoVerify="true">-->
|
||||||
|
<!-- <action android:name="android.intent.action.VIEW" />-->
|
||||||
|
<!-- <category android:name="android.intent.category.DEFAULT" />-->
|
||||||
|
<!-- <category android:name="android.intent.category.BROWSABLE" />-->
|
||||||
|
<!-- <data android:scheme="https" />-->
|
||||||
|
<!-- <data android:host="github.com" />-->
|
||||||
|
<!-- <data android:pathPattern="/rt-bishop/Look4Sat/passes.*" />-->
|
||||||
|
<!-- </intent-filter>-->
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ import androidx.compose.animation.core.rememberInfiniteTransition
|
|||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.scaleIn
|
||||||
|
import androidx.compose.animation.scaleOut
|
||||||
|
import androidx.compose.animation.slideInHorizontally
|
||||||
|
import androidx.compose.animation.slideOutHorizontally
|
||||||
import androidx.compose.animation.togetherWith
|
import androidx.compose.animation.togetherWith
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -32,6 +36,7 @@ import androidx.compose.foundation.layout.Box
|
|||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
@@ -39,11 +44,13 @@ import androidx.compose.foundation.layout.width
|
|||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteDefaults
|
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteDefaults
|
||||||
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
|
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
|
||||||
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType
|
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -62,6 +69,8 @@ import androidx.navigation3.runtime.rememberNavBackStack
|
|||||||
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
|
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
|
||||||
import androidx.navigation3.ui.NavDisplay
|
import androidx.navigation3.ui.NavDisplay
|
||||||
import com.rtbishop.look4sat.core.domain.repository.IContainerProvider
|
import com.rtbishop.look4sat.core.domain.repository.IContainerProvider
|
||||||
|
import com.rtbishop.look4sat.core.presentation.DeeplinkResolver
|
||||||
|
import com.rtbishop.look4sat.core.presentation.RadarDestination
|
||||||
import com.rtbishop.look4sat.core.presentation.Screen
|
import com.rtbishop.look4sat.core.presentation.Screen
|
||||||
import com.rtbishop.look4sat.core.presentation.hasEnoughHeight
|
import com.rtbishop.look4sat.core.presentation.hasEnoughHeight
|
||||||
import com.rtbishop.look4sat.core.presentation.hasEnoughWidth
|
import com.rtbishop.look4sat.core.presentation.hasEnoughWidth
|
||||||
@@ -72,13 +81,47 @@ import com.rtbishop.look4sat.feature.satellites.SatellitesDestination
|
|||||||
import com.rtbishop.look4sat.feature.settings.SettingsDestination
|
import com.rtbishop.look4sat.feature.settings.SettingsDestination
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen() {
|
fun NavRoot(deeplink: String? = null) {
|
||||||
|
val rootBackStack = rememberNavBackStack(Screen.Passes)
|
||||||
|
val deeplinkResolver = DeeplinkResolver()
|
||||||
|
LaunchedEffect(deeplink) {
|
||||||
|
deeplink?.let {
|
||||||
|
val destination = deeplinkResolver.resolve(it) // rootBackStack.clear()
|
||||||
|
rootBackStack.add(destination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val navigateBack: () -> 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<Screen.Passes> { MainScreen(navigateToRadar = { rootBackStack.add(RadarDestination) }) }
|
||||||
|
entry<RadarDestination> {
|
||||||
|
Scaffold { innerPadding ->
|
||||||
|
RadarDestination(navigateUp = navigateBack)
|
||||||
|
innerPadding.calculateTopPadding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MainScreen(navigateToRadar: () -> Unit = {}) {
|
||||||
val backStack = rememberNavBackStack(Screen.Passes)
|
val backStack = rememberNavBackStack(Screen.Passes)
|
||||||
val currentKey = backStack.lastOrNull()
|
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 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 navItems = listOf(Screen.Satellites, Screen.Passes, Screen.Radar, Screen.Map, Screen.Settings)
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -139,6 +182,7 @@ fun MainScreen() {
|
|||||||
PassesDestination { catNum, aosTime ->
|
PassesDestination { catNum, aosTime ->
|
||||||
container.satelliteRepo.selectPass(catNum, aosTime)
|
container.satelliteRepo.selectPass(catNum, aosTime)
|
||||||
backStack.add(Screen.Radar)
|
backStack.add(Screen.Radar)
|
||||||
|
// navigateToRadar()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entry<Screen.Radar> {
|
entry<Screen.Radar> {
|
||||||
|
|||||||
@@ -38,3 +38,33 @@ sealed class Screen(val iconResId: Int, val titleResId: Int) : NavKey {
|
|||||||
@Serializable
|
@Serializable
|
||||||
data object Settings : Screen(R.drawable.ic_settings, R.string.nav_prefs)
|
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<IDeeplinkMatcher> = listOf(PassDetailsMatcher)
|
||||||
|
|
||||||
|
fun resolve(deeplink: String): NavKey {
|
||||||
|
matchers.forEach { it.match(deeplink)?.let { match -> return match } }
|
||||||
|
return fallbackDestination
|
||||||
|
}
|
||||||
|
}
|
||||||
+39
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user